Rust 元组
元组(tuple)可以把几个值组合成一个整体,这些值的类型可以各不相同。想把几样相关的东西快速打包在一起、又不想专门定义一个新类型时,元组就很方便。
fn main() { let person: (&str, i32, bool) = ("小明", 30, true); println!("{:?}", person);}输出:
("小明", 30, true)注意这个元组在一个值里混合了 &str、i32 和 bool。数组做不到这一点,因为数组里的每个元素都必须是同一种类型。
要打印整个元组,用 {:?}(调试格式),而不是 {}。普通的 {} 不能用来打印元组。
按位置访问元素
Section titled “按位置访问元素”用 .0、.1、.2 等等来访问。元组的索引从 0 开始:
fn main() { let person = ("小明", 30, true);
println!("姓名:{}", person.0); println!("年龄:{}", person.1); println!("活跃:{}", person.2);}输出:
姓名:小明年龄:30活跃:true位置必须是直接写在代码里的数字,不能用变量,比如 person.i。因为每个位置上的类型可能都不一样,而 Rust 必须在编译时就知道类型。
一行代码就能把每个值分别取到各自的变量里:
fn main() { let person = ("小明", 30, true); let (name, age, is_active) = person;
println!("{name} 今年 {age} 岁,活跃:{is_active}");}输出:
小明 今年 30 岁,活跃:true这通常比反复写 .0、.1、.2 更清楚,尤其是当元组有三个以上字段的时候。
从函数返回多个值
Section titled “从函数返回多个值”函数只能声明一个返回类型,所以要返回不止一个值时,元组是最自然的选择:
fn min_max(numbers: &[i32]) -> (i32, i32) { let min = *numbers.iter().min().unwrap(); let max = *numbers.iter().max().unwrap(); (min, max)}
fn main() { let (smallest, largest) = min_max(&[4, 8, 1, 9, 3]); println!("最小值:{smallest},最大值:{largest}");}输出:
最小值:1,最大值:9先别管 &[i32]、.iter() 和 .unwrap(),它们会在 slice、迭代器和 Option 这几页讲到。这里要注意的是返回类型 (i32, i32),以及 main 怎样马上把它解构成两个变量。
修改元组与交换值
Section titled “修改元组与交换值”如果元组是用 mut 声明的,就可以用同样的 .0、.1 位置逐个修改它的字段。值可以变,但形状不能变:元组的字段数量和类型永远和一开始一样。
解构也可以反过来用:把值赋给已有的变量。这样一行代码就能交换两个值,不需要临时变量。如果只想要其中一部分字段,不需要的就写 _:
fn main() { let mut point = (3, 4); point.0 += 10; // 原地修改一个字段 println!("point 是 {:?}", point);
let mut a = 1; let mut b = 2; (a, b) = (b, a); // 交换两个值 println!("a = {a}, b = {b}");
let (x, _) = point; // 保留 x,忽略第二个字段 println!("x = {x}");}输出:
point 是 (13, 4)a = 2, b = 1x = 13交换之所以可行,是因为 Rust 会先构造出右边的 (b, a),然后才把它拆开赋给 a 和 b。_ 和模式还有很多用法,可以去模式匹配那一页看看。
空元组:()
Section titled “空元组:()”其实你早就见过一个元组了,只是没意识到。() 叫作单元类型(unit type),当函数不返回任何有意义的值时,返回的就是它。它也是 fn main() 隐含的返回类型。
什么时候用元组,什么时候用结构体
Section titled “什么时候用元组,什么时候用结构体”元组很适合快速、不起名字的组合,尤其是从函数返回两三个值的时候。一旦一组值代表一个真正的概念,需要带名字的字段(比如一个 Person,你会在代码各处按名字使用它的姓名和年龄),用结构体通常更好、更易读。
1. 和与积
Section titled “1. 和与积”写一个函数 sum_and_product(a: i32, b: i32) -> (i32, i32),同时返回两个数的和与积。在 main 里用 5 和 6 调用它,并解构结果。提示:函数的最后一行就是元组本身 (a + b, a * b),不带分号。
查看答案
fn sum_and_product(a: i32, b: i32) -> (i32, i32) { (a + b, a * b)}
fn main() { let (sum, product) = sum_and_product(5, 6); println!("和 = {sum},积 = {product}");}输出:
和 = 11,积 = 302. 由两个角组成的矩形
Section titled “2. 由两个角组成的矩形”矩形可以存成一个由两个点组成的元组,而每个点本身也是一个元组:let rect = ((1, 2), (5, 5));。算出它的宽、高和面积。提示:元组可以嵌套,rect.1.0 的意思是“第二个点的第一个字段”。
查看答案
fn main() { let rect = ((1, 2), (5, 5));
let width = rect.1.0 - rect.0.0; let height = rect.1.1 - rect.0.1;
println!("宽 {width},高 {height},面积 {}", width * height);}输出:
宽 4,高 3,面积 12也可以一次解构两个点:let ((x1, y1), (x2, y2)) = rect;。这样要多打几个字,但 x2 - x1 比 rect.1.0 - rect.0.0 好读得多。当点号像这样越堆越多时,就说明用带命名字段的结构体会更清楚。
- 元组把类型可能不同的值组合在一起:
(name, age, is_active)。 - 用
.0、.1、.2访问字段,或者用解构一次全部取出来。 - 元组是从函数返回多个值的自然方式。
mut元组的字段可以修改,但它的大小和类型不能变。(a, b) = (b, a);可以交换两个值。- 需要有名字、可复用的组合时,改用结构体。
小测验
1. 元组能存放、而数组不能存放的是什么?
2. 怎样访问名为 person 的元组的第二个元素?
3. 执行 let mut a = 1; let mut b = 2; (a, b) = (b, a); 之后,a 和 b 分别是多少?
得分: 0 / 3
- 元组类型 — Rust 程序设计语言:官方教程对元组的创建、解构和索引的介绍。
- 元组 — 通过例子学 Rust:嵌套元组以及把元组作为函数参数的可运行示例。
- The
tupletype — 标准库文档(英文):元组支持哪些 trait,比如比较、复制和用{:?}打印。 - The
unittype — 标准库文档(英文):()是什么,以及它会出现在哪里。 - Destructuring assignments — The Rust Reference(英文):
(a, b) = (b, a);背后的精确规则。