Rust从0️⃣入门(3)

3,176 阅读3分钟

基础语法

继续trait

  • 我们也可以利用trait给其他的类型添加成员方法
    • 如下代码,i32根本不是我们写的
    • 但是我们可以给i32增加方法

trait Double {
    fn double(&self) -> Self;
}



impl Double for i32 {
    fn double(&self) -> i32 {
        *self * 2
    }
}


fn main() {
    let x : i32 = 10.double();
    //输出 double类型的值为 20
    println!("double类型的值为 {}",x)
}

通用函数调用语法


trait Cook {
    fn start(&self);
}


trait Wash {
    fn start(&self);
}

struct Chef;

impl Cook for Chef {
    fn start(&self) {
        println!("开始烹饪!!");
    }
}

impl Wash for Chef {
    fn start(&self) {
        println!("开始刷碗!!");
    }
}



fn main() {
    let me = Chef;
    //输出 
    // 开始烹饪!!
    // 开始刷碗!!
    <Cook>::start(&me);
    <Chef as Wash>::start(&me);
}
  • 以上代码之所以在main中,具体写到两个trait去调用
    • 是因为如果只写一句me.start()会产生二义性
    • 所以在调用的时候一定要注意,要对应好是哪个trait

泛型


fn test<T : Debug>(x : T) {
   println!("this is {:?}.",x);
}


fn main() {
   /*
   this is "Test".
   this is 77.
   this is true.
   this is ['n', 'a', 's', 's'].
   */
   test("Test");
   test(77i32);
   test(true);
   test(['n','a','s','s']);
}
  • 以上代码,其中{:?}为格式化控制符 数组

fn main() {

    let a: [i32; 3] = [1,2,3];
    let b: [i32; 10] = [0; 10];

    //1 2 3
    for i in a {
        print!(" {}",i);
    }
    // 0 0 0 0 0 0 0 0 0 0
    for i in b {
        print!(" {}",i);
    }
}
  • 以上代码:要注意数组长度、数组类型
    • 数组b是赋值了十个0 数组切片
  • 我们可以将数组切片看做专门指向数组的指针
    • 可以理解成数组的一个视图
    • 例子中的数组是[T; n]类型的
    • 指针类型是&[T; n],通过内部编译将数组类型转换为切片类型&[T]
fn main() {

    fn mut_arr(a : &mut [i32]) {
        a[2] = 5;
    }

    println!("size of &[i32; 3] : {:?}", std::mem::size_of::<&[i32; 3]>());
    println!("size of &[i32]  : {:?}", std::mem::size_of::<&[i32]>());

    let mut v : [i32; 3] = [1,2,3];
    {
        let s : &mut [i32; 3] = &mut v;
        mut_arr(s);
    }
    // 输出为:
    // size of &[i32; 3] : 8
    // size of &[i32]  : 16
    // [1, 2, 5]
    println!("{:?}",v);
}
  • 上图代码其中变量v[i32; 3]类型
    • 变量s&mut [i32; 3]类型,这是占用的空间大小与指针都是相同的
    • 当自动转为&mut [i32; 3]类型时,传入函数mut_arr
    • 根据输出可以看出,占用的空间大小等于两个指针的空间大小
    • 在函数的内部,修改了外部数组的v的值

胖指针与DST

  • 因为数组切片不只是包含指向数组的指针,切片本身还包含带长度的信息,所以叫胖指针
    • 胖纸真对应的是动态大小类型可简称缩写DST

    • 比如例子中用到的是不定长数组类型是[T],对应的胖指针类型是&[T]

    • 由于无法判断类型占用的空间的大小

    • 所以不能在栈上声明一个不定长大小数组的变量实例

fn example_slice(arr: &[i32]) {
    unsafe {
        let (v1, v2) : (usize,usize) = std::mem::transmute(arr);

        println!("Value1 is {:x}",v1);
        println!("Value2 is {:x}",v2);
    }
}

fn main() {

    let arr : [i32; 5] = [1,2,3,4,5];
    let addr : &[i32; 5] = &arr;
    println!("Address of arr is: {:p}",addr);
    // 输出为:
    // Address of arr is: 0x7ffee759f424
    // Value1 is 7ffee759f424
    // Value2 is 5
    example_slice(addr as &[i32]);
}
  • 在上面代码中,arr的长度是5
    • 其中addr是指向arr的一个指针
    • 使用函数将我们的数组直接转为了一个长度和一个指向源数组的地址

Range

  • Range代表一个区间
    • 使用..代表左开右闭的区间
    • 1..7代表1到7
fn main() {

    let a = 1..7;
    
    // 1   2  3  4  5  6
    for i in a {
        print!("{:?}\t",i);
    }
}