Skip to content

数据类型 ​

Rust 是静态类型语言:编译期就必须确定每个值的类型。多数情况下编译器可以自动推断,只在歧义处(如 parse() 的目标类型)要求显式标注 —— 猜数字游戏里的 let guess: u32 = ... 就是一例。

类型分两大类:标量类型(单个值)与复合类型(打包多个值)。

1. 标量类型 ​

整数 ​

按"有无符号 + 长度"组合出 12 种:

长度有符号无符号
8 位i8u8
16 位i16u16
32 位i32u32
64 位i64u64
128 位i128u128
随架构isizeusize
  • 有符号(i = integer)可为负:i8 范围 −128 ~ 127;无符号(u = unsigned)从 0 开始:u8 范围 0 ~ 255
  • 未标注类型时编译器默认推断为 i32
  • isize / usize 的位数跟随运行平台(64 位机上为 64 位),主要用于集合的索引

整数字面量的多种写法:

rust
let a = 98_222;      // 十进制,下划线提高可读性
let b = 0xff;        // 十六进制
let c = 0o77;        // 八进制
let d = 0b1111_0000; // 二进制
let e = b'A';        // 字节(u8),取字符的 ASCII 值

取值范围不必背 —— 标准库为每个整数类型提供了 MAX / MIN 关联常量,直接问编译器(来自 exercises/0004-data-types/src/main.rs):

rust
println!("{}", i8::MAX); // 127
println!("{}", i8::MIN); // -128

println!("{}", i64::MAX); // 9223372036854775807
println!("{}", isize::MAX); // 9223372036854775807(64 位平台上与 i64 相同)

整数溢出:debug 构建下会直接 panic;release 构建下按环绕规则处理(255 + 10 环绕为 9)。实测:

rust
let mut x: u8 = 255;
x += 10; // debug 构建下在此处 panic
println!("x={}", x); // release 构建下才会执行到:环绕后 x=9

在 exercises/ 下运行 cargo run -p lesson_0004,程序最后会以 attempt to add with overflow panic —— 这就是 debug 构建在保护你。需要显式控制溢出行为时,用标准库提供的 checked_add 等方法。

浮点数 ​

f32(单精度)与 f64(双精度),均按 IEEE 754 标准实现,默认推断为 f64(现代 CPU 上两者速度相当,f64 精度更高)。浮点类型全部有符号,没有无符号浮点。

rust
let x = 2.5;       // f64
let y: f32 = 3.0;  // f32
let big = 2e2;     // 200.0:科学计数法

浮点有四个和直觉不同的行为,全部源自 IEEE 754 的设计,也是面试和事故的高发区:

① 除零不崩溃,而是得到特殊值。 整数 7 / 0 直接编译错误;浮点 1.0 / 0.0 得到 inf(正无穷),-1.0 / 0.0 得到 -inf。

② 表示精度有限。 小数在二进制下大多无法精确表示,0.1 + 0.2 的结果是 0.30000000000000004,与 0.3 比较结果为 false。判断浮点相等应改用误差范围:(a - b).abs() < 1e-10。

③ NaN(非数字)不等于任何值,包括它自己。 0.0 / 0.0 得到 NaN,此后 nan == nan 为 false,判断必须用 is_nan() 方法。NaN 还会传染:与 NaN 参与运算的结果仍是 NaN。

④ 只实现 PartialEq,没有 Eq 和 Hash。 后果:浮点数不能作为 HashMap 的键,也不能用于 match 匹配 —— 因为 NaN 的存在让"相等"不再可靠。

实践建议:金额等要求精确十进制的场景不要用浮点(用整数最小单位如"分"存储,或引入 decimal 库)。

真实代码验证(exercises/0004-data-types/src/main.rs,运行 cargo run -p lesson_0004 观察输出,注意最后一组整数溢出会以 panic 结尾):

rust
// 精度陷阱:IEEE 754 的固有表示误差
let sum = 0.1 + 0.2;
println!("0.1 + 0.2 = {}", sum); // 0.30000000000000004
println!("(0.1 + 0.2) == 0.3 -> {}", sum == 0.3); // false

// 除零不 panic,而是得到特殊值(对比整数:除零直接编译错误)
println!("1.0 / 0.0 = {}", 1.0 / 0.0); // inf
println!("-1.0 / 0.0 = {}", -1.0 / 0.0); // -inf

// NaN:非数字。它与任何值比较都是 false,包括与自身
let nan = f64::NAN;
#[allow(clippy::eq_op)] // 故意用相同操作数演示 NaN 行为
let eq = nan == nan;
println!("nan == nan -> {}", eq); // false
println!("nan.is_nan() -> {}", nan.is_nan()); // true:判断 NaN 要用专门方法

布尔 ​

bool 只有两个值 true / false,占 1 字节。猜数字游戏里 Ordering 的匹配逻辑本质上就是在处理"三态比较结果"。

字符 ​

char 用单引号,占 4 字节,是一个 Unicode 标量值 —— 中文字符、字母都是合法的 char:

rust
let letter = 'z';
let chinese = '中';

注意与字符串的区别:char 用单引号,字符串(&str / String)用双引号。

数值运算 ​

rust
let sum = 5 + 10;        // 15
let difference = 95 - 4; // 91
let product = 4 * 30;    // 120
let quotient = 7 / 2;    // 3:整数除法向零截断
let remainder = 7 % 2;   // 1:取余

重点:整数除法会截断小数(7 / 2 得 3 不是 3.5),需要小数结果时用浮点类型。

2. 复合类型 ​

元组(tuple) ​

把多个不同类型的值打包,长度固定:

rust
let tup: (i32, f64, char) = (500, 6.4, '中');

// 解构:拆成三个变量
let (x, y, z) = tup;

// 点索引访问
let first = tup.0; // 500

解构在猜数字游戏里已出现过雏形 —— let (a, b) = pair; 一次性取出两个值。

没有任何元素的元组 () 称为单元类型:当一个函数"没有返回值"时,它返回的就是 ()。

数组(array) ​

多个同类型值的固定长度集合,分配在栈上:

rust
let arr: [u32; 5] = [1, 2, 3, 4, 5];   // 类型标注:[元素类型; 长度]
let same = [3; 5];                      // [3, 3, 3, 3, 3]:5 个 3

let first = arr[0]; // 索引从 0 开始

数组长度是类型的一部分([u32; 5] 与 [u32; 6] 是不同类型)。索引越界会在运行期触发 panic —— 与 C/C++ 的未定义行为不同,Rust 选择了安全地崩溃。

需要"长度可变的列表"时用 Vec(后续集合课程)。

3. 参考 ​