3. 基本类型与类型推断边界实验

基本类型与类型推断边界实验 学习卡片

类型推断的边界在哪里

Rust 的类型推断是局部的、基于约束的:编译器会收集变量使用过程中产生的类型约束,再求解出唯一类型。一旦约束不足(例如只声明、不参与任何运算或函数调用),推断就会失败,必须显式标注。下面这段程序把整数、浮点、布尔、字符、元组、数组都放进来,并故意制造一个推断失败点。

fn main() {
    // 1. 整数:默认 i32,但可被后续使用"拉"成其他类型
    let a = 10;                 // 推断为 i32
    let b: u8 = 200;            // 显式标注 u8
    let c = a + b as i32;       // as 转换后参与运算

    // 2. 浮点:默认 f64
    let pi = 3.14159;           // f64
    let ratio: f32 = 0.5;       // 显式 f32

    // 3. 布尔与字符
    let flag = true;            // bool
    let grade = 'A';            // char,4 字节 Unicode 标量

    // 4. 元组:异构、定长
    let pair: (i32, char) = (7, 'z');
    let (x, y) = pair;          // 解构

    // 5. 数组:同构、定长,类型为 [T; N]
    let arr: [i32; 3] = [1, 2, 3];
    let first = arr[0];

    // 6. 推断失败场景:只声明、不使用,编译器无法确定类型
    // let mystery;             // 编译错误:type annotations needed
    let mystery: i64 = 0;       // 显式标注后通过

    println!("{a} {b} {c} {pi} {ratio} {flag} {grade}");
    println!("{x} {y} {first} {mystery}");
}

把 let mystery; 单独写出来,编译器会报 E0282: type annotations needed。原因是该变量没有任何使用点提供约束,推断器无法在候选类型中做出唯一选择。显式写成 let mystery: i64 = 0; 即可通过。

溢出行为与 as 转换规则

Rust 的整数溢出在 debug 与 release 下行为不同:debug 构建会 panic,release 构建默认按二进制补码回绕(wrapping)。as 转换则始终是截断/回绕语义,不做溢出检查。

fn main() {
    let big: u8 = 255;
    // let overflow = big + 1;  // debug: panic 'attempt to add with overflow'
    let wrapped = big.wrapping_add(1);   // 显式回绕,结果 0
    println!("wrapped = {wrapped}");

    // as 转换:截断高位
    let n: i32 = 300;
    let as_u8 = n as u8;                 // 300 % 256 = 44
    println!("300 as u8 = {as_u8}");

    // 负数转无符号:按补码位模式重解释
    let neg: i8 = -1;
    let as_u8_neg = neg as u8;           // 0xFF = 255
    println!("-1i8 as u8 = {as_u8_neg}");

    // 浮点转整数:向零截断
    let f = 3.99_f64;
    let as_int = f as i32;               // 3
    println!("3.99 as i32 = {as_int}");
}
debug 构建下 big + 1 会 panic;release 构建下默认回绕为 0。若希望两种构建行为一致,使用 wrapping_add、checked_add 或 saturating_add 显式表达意图。
类型推断边界与数值转换行为 推断成功:有使用点提供约束 let a = 10; let pi = 3.14; let flag = true; let grade = 'A'; 默认:i32 / f64 / bool / char 元组 (i32, char)、数组 [i32; 3] 同理 推断失败:无使用点,约束不足 let mystery; // E0282 let mystery: i64 = 0; // 显式标注通过 编译器无法在候选类型中唯一选择 必须由程序员显式指定 溢出行为与 as 转换规则 整数溢出 debug: panic | release: 回绕 255u8 + 1 → 0(wrapping_add) as 转换:截断 / 位模式重解释 300i32 as u8 → 44(300 % 256) -1i8 as u8 → 255 | 3.99f64 as i32 → 3 显式方法:wrapping / checked / saturating as 不做溢出检查,需自行保证范围

验证与观察要点

  • 用 cargo build(debug)运行含 big + 1 的版本,观察 panic 信息中的 attempt to add with overflow。
  • 用 cargo build --release 再运行,观察同一表达式回绕为 0,验证 debug/release 差异。
  • 把 let mystery; 恢复为未标注形式,确认编译器报 E0282 并提示需要类型标注。
  • 对 as 转换,分别打印 300i32 as u8、-1i8 as u8、3.99f64 as i32,核对截断与位模式重解释结果。
类型推断只在局部作用域内进行,不会跨函数签名反向推断。函数参数和返回值必须显式标注类型,这也是 Rust 推断边界的重要体现。