变量

  1. 使用 let 关键字。
  2. 默认不可变,加 mut(mutable)为可变。
  3. 不必显性声明数据类型。
  4. 遮蔽 (shadow):使用相同的变量名并重复使用 let 关键字遮蔽变量。
  1. let x = 5;
  2. x = 6; // error
  3. let x = x + 1; // shadow, this 'x' is 6
  4. let mut x = "hello"; // shadow

常量

  1. const THREE_HOURS_IN_SECONDS: u32 = 60 * 60 * 3;
  1. 使用 const 关键字。
  2. 不可变,所以不能使用 mut。
  3. 必须显性声明数据类型。
  4. 常量只能设置为常量表达式,而不能是运行时计算的结果,如调用函数得到的值。

数据类型

  1. Rust 是静态类型(statically typed)语言,这意味着它必须在编译期知道所有变量的类型。
  2. 通常可以自动推导出类型,但某些情况需人为指定。
  1. // default typed
  2. let x = 5; // i32
  3. // 人工确认u32,否则编译不通过。
  4. let guess: u32 = "42".parse().expect("Not a number!");

标量类型

  1. 标量(scalar)类型表示单个值。Rust 有 4 个基本的标量类型:整型、浮点型、布尔字符
整型
  1. 默认整型是 i32。
  2. 有符号以 i(integer)开始,无符号以 u (unsigned)开始。有符号类型的范围是 -(2n - 1) ~ 2n - 1 - 1。
  3. isize 和 usize 取决于操作系统, isize 在64位操作系统中等于i64,32位中等于 i32,主要应用场景是用作某些集合的索引。
  4. 可以使用字面量声明。例:十进制 1_000 = 1000,十六进制 0x41 = 字节 “A” = 65 // TODO
字节 有符号 范围 无符号 范围
2 i8 -128~127 u8 0~255
4 i16 //TODO u16
6 i32 u32
8 i64 u64
10 i128 u128
4/8 isize usize
  1. let x = 1; // i32
  2. let x = "A"; // i32 //TODO
  3. let x: i64 = 1_000; // i64

整数溢出

当存储的数据大于数据类型的最大限度后会出现整型溢出(integer overflow),有两种情况:

  1. 调试(debug)模式编译时会自动检查,若存在该问题则编译时 panic。
  2. 发布(release)模式构建时不检测。如果发生会二进制补码包裹(two’s complement wrapping)。
    大于该类型最大值的数值会被“包裹”成该类型能够支持的对应数字的最小值。例 u8,256 变成 0,257 变成 1,依此类推。

:::warning ⚠️依赖整型溢出包裹的行为不是一种正确的做法。因为该变量的值可能不是期望的值。

:::

要显式处理溢出的可能性,可以使用标准库针对原始数字类型提供的以下一系列方法:

  • 使用 wrapping_* 方法在所有模式下进行包裹,例如 wrapping_add
  • 如果使用 checked_* 方法时发生溢出,则返回 None 值
  • 使用 overflowing_* 方法返回该值和一个指示是否存在溢出的布尔值
  • 使用 saturating_* 方法使值达到最小值或最大值
浮点型
  1. 默认浮点型是 f64。因为现代 CPU 的速度与 f32 的几乎相同,但精度更高。
  2. 所有浮点型都是有符号的。
  3. 浮点数按照 IEEE-754 标准表示,所以运算时需确保精度的准确。
字节 名称 小数点位数
4 f32(单精度浮点) 6
8 f64(双精度浮点) 12
  1. let x = 2.0; // f64
  2. let y: f32 = 3.0; // f32
布尔
  1. true 和 false,大小为 1 个字节。
  1. let t = true;
  2. let f: bool = false;
字符
  1. 大小为 4 个字节,表示的是一个 Unicode 标量值。例:标音字母、中/日/韩文、emoji。
  1. let c = 'z';
  2. let z = 'ℤ';
  3. let heart_eyed_cat = '😻';

复合类型

元组(tuple)
  1. 元组是将多种类型的多个值组合到一个复合类型中的一种基本方式。
  2. 长度固定,声明后无法增长或缩小。
  1. let tup: (i32, f64, u8) = (500, 6.4, 1);
  2. let tup = (500, 6.4, 1);
  3. // use mode: destructuring
  4. let (x, y, z) = tup; // 解构
  5. println!("The value of y is: {}", y);
  6. // use mode: index
  7. println!("The value of y is: {}", tup.1);

元组也可以完成多返回值的功能。

  1. fn main() {
  2. let (res1, res2) = f1(1);
  3. }
  4. fn calculate_length(i: i32) -> (i32, i32) {
  5. (1, 2)
  6. }
数组(array)
  1. 将多个值组合在一起且都是同一数据类型的结构为数组。
  2. 长度固定,声明后无法增长或缩小。这样可以分配到内存的栈(stack)而不是堆(heap),从而提高性能。

⚠️ 如果想长度动态,可使用 vector 动态数组

  1. 它类似标准库中提供的集合类型,其大小允许增长或缩小。
  2. 如果不确定是使用 array 还是 vector,那你就选择 vector。