• 变量绑定与原生类型
    • 变量绑定
    • 可变绑定
    • let解构
    • 原生类型

    变量绑定与原生类型

    变量绑定

    Rust 通过 let 关键字进行变量绑定。

    1. fn main() {
    2. let a1 = 5;
    3. let a2:i32 = 5;
    4. assert_eq!(a1, a2);
    5. //let 绑定 整数变量默认类型推断是 i32
    6. let b1:u32 = 5;
    7. //assert_eq!(a1, b1);
    8. //去掉上面的注释会报错,因为类型不匹配
    9. //errer: mismatched types
    10. }

    这里的 assert_eq! 宏的作用是判断两个参数是不是相等的,但如果是两个不匹配的类型,就算字面值相等也会报错。

    可变绑定

    rust 在声明变量时,在变量前面加入 mut 关键字,变量就会成为可变绑定的变量。

    1. fn main() {
    2. let mut a: f64 = 1.0;
    3. let b = 2.0f32;
    4. //改变 a 的绑定
    5. a = 2.0;
    6. println!("{:?}", a);
    7. //重新绑定为不可变
    8. let a = a;
    9. //不能赋值
    10. //a = 3.0;
    11. //类型不匹配
    12. //assert_eq!(a, b);
    13. }

    这里的 b 变量,绑定了 2.0f32。这是 Rust 里面值类型显式标记的语法,规定为value+type的形式。

    例如:
    固定大小类型:

    1u8 1i8
    1u16 1i16
    1u32 1i32
    1u64 1i64

    可变大小类型:

    1usize 1isize

    浮点类型:

    1f32 1f64

    let解构

    为什么在 Rust 里面声明一个变量的时候要采用 let 绑定表达式?
    那是因为 let 绑定表达式的表达能力更强,而且 let 表达式实际上是一种模式匹配。

    例如:

    1. fn main() {
    2. let (a, mut b): (bool,bool) = (true, false);
    3. println!("a = {:?}, b = {:?}", a, b);
    4. //a 不可变绑定
    5. //a = false;
    6. //b 可变绑定
    7. b = true;
    8. assert_eq!(a, b);
    9. }

    这里使用了 bool,只有true和false两个值,通常用来做逻辑判断的类型。

    原生类型

    Rust内置的原生类型 (primitive types) 有以下几类:

    • 布尔类型:有两个值truefalse
    • 字符类型:表示单个Unicode字符,存储为4个字节。
    • 数值类型:分为有符号整数 (i8, i16, i32, i64, isize)、
      无符号整数 (u8, u16, u32, u64, usize) 以及浮点数 (f32, f64)。
    • 字符串类型:最底层的是不定长类型str,更常用的是字符串切片&str和堆分配字符串String
      其中字符串切片是静态分配的,有固定的大小,并且不可变,而堆分配字符串是可变的。
    • 数组:具有固定大小,并且元素都是同种类型,可表示为[T; N]
    • 切片:引用一个数组的部分数据并且不需要拷贝,可表示为&[T]
    • 元组:具有固定大小的有序列表,每个元素都有自己的类型,通过解构或者索引来获得每个元素的值。
    • 指针:最底层的是裸指针*const T*mut T,但解引用它们是不安全的,必须放到unsafe块里。
    • 函数:具有函数类型的变量实质上是一个函数指针。
    • 元类型:即(),其唯一的值也是()
    1. // boolean type
    2. let t = true;
    3. let f: bool = false;
    4. // char type
    5. let c = 'c';
    6. // numeric types
    7. let x = 42;
    8. let y: u32 = 123_456;
    9. let z: f64 = 1.23e+2;
    10. let zero = z.abs_sub(123.4);
    11. let bin = 0b1111_0000;
    12. let oct = 0o7320_1546;
    13. let hex = 0xf23a_b049;
    14. // string types
    15. let str = "Hello, world!";
    16. let mut string = str.to_string();
    17. // arrays and slices
    18. let a = [0, 1, 2, 3, 4];
    19. let middle = &a[1..4];
    20. let mut ten_zeros: [i64; 10] = [0; 10];
    21. // tuples
    22. let tuple: (i32, &str) = (50, "hello");
    23. let (fifty, _) = tuple;
    24. let hello = tuple.1;
    25. // raw pointers
    26. let x = 5;
    27. let raw = &x as *const i32;
    28. let points_at = unsafe { *raw };
    29. // functions
    30. fn foo(x: i32) -> i32 { x }
    31. let bar: fn(i32) -> i32 = foo;

    有几点是需要特别注意的:

    • 数值类型可以使用_分隔符来增加可读性。
    • Rust还支持单字节字符b'H'以及单字节字符串b"Hello",仅限制于ASCII字符。
      此外,还可以使用r#"..."#标记来表示原始字符串,不需要对特殊字符进行转义。
    • 使用&符号将String类型转换成&str类型很廉价,
      但是使用to_string()方法将&str转换到String类型涉及到分配内存,
      除非很有必要否则不要这么做。
    • 数组的长度是不可变的,动态的数组称为Vec (vector),可以使用宏vec!创建。
    • 元组可以使用==!=运算符来判断是否相同。
    • 不多于32个元素的数组和不多于12个元素的元组在值传递时是自动复制的。
    • Rust不提供原生类型之间的隐式转换,只能使用as关键字显式转换。
    • 可以使用type关键字定义某个类型的别名,并且应该采用驼峰命名法。
    1. // explicit conversion
    2. let decimal = 65.4321_f32;
    3. let integer = decimal as u8;
    4. let character = integer as char;
    5. // type aliases
    6. type NanoSecond = u64;
    7. type Point = (u8, u8);