4. 基础数据类型:标量类型与复合类型
数据类型这东西,说白了就是告诉编译器「我要存什么样的数据」。Rust 是静态类型语言,每个变量在编译时就必须知道自己的类型。我刚开始学的时候觉得这很烦,但后来发现——正是这种严格,帮我挡住了无数运行时崩溃。
4.1 标量类型:最基础的积木
标量类型代表单个值。Rust 有四种:整数、浮点数、布尔、字符。
4.1.1 整数类型
整数分有符号(i)和无符号(u),后面跟位数。比如 i32 是 32 位有符号整数,u8 是 8 位无符号整数。
| 长度 | 有符号 | 无符号 |
|---|---|---|
| 8 位 | i8 | u8 |
| 16 位 | i16 | u16 |
| 32 位 | i32 | u32 |
| 64 位 | i64 | u64 |
| 128 位 | i128 | u128 |
| arch | isize | usize |
默认整数类型是 i32。我个人习惯在嵌入式场景用 u8 或 u16 省内存,普通应用直接用 i32 最省心。
let a = 42; // 默认 i32
let b: u8 = 255; // 显式标注 u8
let c = 0xFF; // 十六进制
let d = 0o77; // 八进制
let e = 0b1010; // 二进制
let f = 100_000; // 下划线分隔,方便阅读
wrapping_add 或 checked_add。
4.1.2 浮点数类型
两种:f32(单精度)和 f64(双精度)。默认是 f64,因为现代 CPU 上两者速度几乎一样,但精度更高。
let x = 2.0; // f64
let y: f32 = 3.14; // f32
嗯,这里要注意:浮点数没有实现 Eq 和 Ord trait。你不能直接用 == 比较两个浮点数是否相等。为什么?因为 NaN 不等于任何值,包括它自己。
(a - b).abs() < 1e-10 这种容差比较,而不是直接 a == b。
4.1.3 布尔类型
就两个值:true 和 false。大小是 1 个字节。
let is_ready = true;
let is_done: bool = false;
你想想看,布尔值在条件判断里用得最多。但 Rust 里有个小细节:if 条件必须是 bool 类型,不会像 C 语言那样把非零值当 true。
4.1.4 字符类型
char 类型代表一个 Unicode 标量值,占 4 个字节。它比 C 语言的 char 强大得多——支持中文、emoji、甚至古埃及文字。
let c = 'z';
let z = '中';
let emoji = '😊';
注意用单引号,不是双引号。双引号是字符串。
4.2 复合类型:把积木拼起来
复合类型把多个值组合成一个类型。Rust 有两种内置的复合类型:元组和数组。
4.2.1 元组(Tuple)
元组可以包含不同类型的值,长度固定。一旦声明,不能增加或删除元素。
let tup: (i32, f64, u8) = (500, 6.4, 1);
// 解构
let (x, y, z) = tup;
// 索引访问
let first = tup.0; // 500
let second = tup.1; // 6.4
我在项目中常用元组返回多个值。比如一个函数返回 (状态码, 数据),比定义结构体更轻量。
(42,),否则会被解析为普通括号表达式。
4.2.2 数组(Array)
数组里所有元素类型相同,长度固定。存在栈上,不是堆上。
let arr: [i32; 5] = [1, 2, 3, 4, 5];
// 简写:所有元素相同
let zeros = [0; 10]; // [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
// 访问
let first = arr[0];
数组越界访问会在运行时 panic。我见过有人用 arr[10] 访问长度为 5 的数组,程序直接崩溃。Rust 不会像 C 那样给你一个野指针,而是直接终止——虽然粗暴,但安全。
Vec<T> 而不是数组。数组适合固定大小的场景,比如 RGB 颜色 [u8; 3]。
4.3 类型推断与注解
Rust 编译器很聪明,能根据上下文推断类型。但有时候它猜不到,就需要你手动标注。
// 编译器能推断
let guess = "42".parse().expect("Not a number!");
// 但这样会报错——它不知道 parse 成什么类型
// 需要显式标注
let guess: u32 = "42".parse().expect("Not a number!");
类型注解的语法是 变量名: 类型。我个人习惯在以下场景显式标注:
- 函数返回值类型(必须标注)
parse()等泛型方法- 复杂表达式,标注后代码更清晰
4.4 知识体系总览
下面这张图帮你理清本章的核心脉络:
4.5 避坑指南
- 整数溢出:debug 模式 panic,release 模式回绕。用
checked_add安全处理。 - 浮点数比较:别用
==,用容差比较。 - 数组越界:运行时 panic,不是编译时。用
get()方法安全访问。 - 类型推断失败:编译器会报错,加上类型注解即可。
好了,这一章的内容就这些。数据类型是 Rust 的基石,搞懂了后面学所有权、泛型都会轻松很多。去写点代码练练手吧——光看是学不会的。