4、复合数据类型:元组与数组
好,咱们今天聊聊 Rust 里的复合数据类型。说白了,就是把多个值打包在一起。Rust 提供了两种最基本的方式:元组和数组。这两种东西看着像,但用起来差别挺大的。
4.1 元组(Tuple)的定义与解构
元组,你可以把它想象成一个「小包裹」。里面可以装不同类型的东西。比如一个整数、一个浮点数、一个布尔值,全塞进去没问题。
let person: (i32, f64, bool) = (25, 1.75, true);
我个人习惯用元组来返回多个值。比如写一个函数,既要返回计算结果,又要返回状态码,元组就特别顺手。
访问元组元素,有两种方式:
- 点号索引:
person.0、person.1、person.2 - 解构:把元组拆开,赋值给多个变量
let (age, height, is_student) = person;
println!("年龄:{},身高:{},是否学生:{}", age, height, is_student);
解构这个特性,我在项目中经常用来简化代码。比如从数据库查出一条记录,直接解构成几个变量,比一个个取索引清爽多了。
_ 忽略不需要的部分。比如
let (age, _, _) = person;,只取年龄,剩下的不要了。
4.2 数组(Array)的定义与访问
数组和元组最大的区别是:数组里所有元素类型必须相同。而且数组的长度是固定的,编译时就确定了。
let numbers: [i32; 5] = [1, 2, 3, 4, 5];
这里 [i32; 5] 表示「5 个 i32 类型的元素」。你还可以用简写方式:
let zeros = [0; 10]; // 创建 10 个 0,等价于 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
访问数组元素用方括号索引:
let first = numbers[0]; // 1
let third = numbers[2]; // 3
嗯,这里要注意:数组索引从 0 开始,这个和大多数语言一样。
4.3 数组越界的运行时行为
这是 Rust 一个非常硬核的地方。你想想看,在 C 语言里数组越界了,程序可能继续跑,但数据已经乱了。Rust 不跟你玩这套。
let arr = [10, 20, 30];
let index = 5;
println!("{}", arr[index]); // 运行时 panic!
我曾经在写一个网络协议解析器时,因为没仔细检查数据包长度,导致数组越界。Rust 直接 panic 了。当时觉得「这也太严格了吧」,但事后想想,这比数据被静默破坏要好一万倍。
为什么会这样?因为 Rust 的设计哲学是:宁可程序崩溃,也不让未定义行为发生。数组越界在 C/C++ 里是未定义行为,可能引发安全漏洞。Rust 直接把它掐死在摇篮里。
如果你不确定索引是否安全,可以用 get 方法:
match arr.get(5) {
Some(value) => println!("找到了:{}", value),
None => println!("索引越界了,但程序没崩溃"),
}
这样就不会 panic 了,而是返回一个 Option 类型,让你自己决定怎么处理。
4.4 元组 vs 数组:一张表说清楚
| 特性 | 元组 (Tuple) | 数组 (Array) |
|---|---|---|
| 元素类型 | 可以不同 | 必须相同 |
| 长度 | 固定,编译时确定 | 固定,编译时确定 |
| 访问方式 | tuple.0、tuple.1 |
arr[0]、arr[1] |
| 解构 | 支持 | 不支持(但可以用模式匹配) |
| 越界检查 | 不适用(索引固定) | 运行时检查,越界则 panic |
| 典型用途 | 返回多个值、临时打包 | 固定大小的数据集合 |
4.5 本章知识结构图
下面这张图帮你理清元组和数组的核心脉络:
说白了,元组和数组都是「打包」数据的方式,但适用场景不同。元组灵活,可以混装不同类型;数组规整,适合批量处理相同类型的数据。而 Rust 对数组越界的零容忍态度,正是它内存安全承诺的体现。
• 元组用
. 访问,数组用 [] 访问• 元组支持解构,数组不支持
• 数组越界会 panic,用
get() 可以安全访问• 两种类型长度都在编译时固定,没有动态增长的版本(那是 Vec 的事)
公众号:蓝海资料掘金营,微信deep3321