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 显式表达意图。验证与观察要点
- 用
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 推断边界的重要体现。