4. 基础数据类型:标量类型与复合类型

数据类型这东西,说白了就是告诉编译器「我要存什么样的数据」。Rust 是静态类型语言,每个变量在编译时就必须知道自己的类型。我刚开始学的时候觉得这很烦,但后来发现——正是这种严格,帮我挡住了无数运行时崩溃。

4.1 标量类型:最基础的积木

标量类型代表单个值。Rust 有四种:整数、浮点数、布尔、字符。

4.1.1 整数类型

整数分有符号(i)和无符号(u),后面跟位数。比如 i32 是 32 位有符号整数,u8 是 8 位无符号整数。

长度有符号无符号
8 位i8u8
16 位i16u16
32 位i32u32
64 位i64u64
128 位i128u128
archisizeusize

默认整数类型是 i32。我个人习惯在嵌入式场景用 u8u16 省内存,普通应用直接用 i32 最省心。

let a = 42;          // 默认 i32
let b: u8 = 255;     // 显式标注 u8
let c = 0xFF;        // 十六进制
let d = 0o77;        // 八进制
let e = 0b1010;      // 二进制
let f = 100_000;     // 下划线分隔,方便阅读
⚠ 我曾经踩过的坑:整数溢出。在 debug 模式下 Rust 会 panic,release 模式下会回绕(wrapping)。如果你需要明确处理溢出,用 wrapping_addchecked_add

4.1.2 浮点数类型

两种:f32(单精度)和 f64(双精度)。默认是 f64,因为现代 CPU 上两者速度几乎一样,但精度更高。

let x = 2.0;         // f64
let y: f32 = 3.14;   // f32

嗯,这里要注意:浮点数没有实现 EqOrd trait。你不能直接用 == 比较两个浮点数是否相等。为什么?因为 NaN 不等于任何值,包括它自己。

💡 我的做法:比较浮点数时用 (a - b).abs() < 1e-10 这种容差比较,而不是直接 a == b

4.1.3 布尔类型

就两个值:truefalse。大小是 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 知识体系总览

下面这张图帮你理清本章的核心脉络:

Rust 数据类型 标量类型 整数 i32/u8 浮点数 f64 布尔 bool 字符 char 复合类型 元组 (i32, f64) 数组 [i32; 5] 类型推断与注解 编译器自动推断 / 手动标注

4.5 避坑指南

  • 整数溢出:debug 模式 panic,release 模式回绕。用 checked_add 安全处理。
  • 浮点数比较:别用 ==,用容差比较。
  • 数组越界:运行时 panic,不是编译时。用 get() 方法安全访问。
  • 类型推断失败:编译器会报错,加上类型注解即可。

好了,这一章的内容就这些。数据类型是 Rust 的基石,搞懂了后面学所有权、泛型都会轻松很多。去写点代码练练手吧——光看是学不会的。


专注资料整理