元组(Tuple) 是一种轻量级、固定长度的异构数据聚合类型,用于将多个不同类型的值组合成一个单一实体。是 Rust 中最常用的 "临时数据容器" 之一:
- 固定长度:创建后无法增减元素,长度是类型的一部分(例如
(i32, &str)和(i32, &str, bool)是完全不同的类型); - 异构类型:元素可以是任意类型(如整数、字符串、结构体、甚至其他元组);
- 值语义:元组是值类型,会被直接放在 栈上;其Copy语义完全取决于内部元素:
- 所有元素都实现
Copy,元组自动是 Copy,可拷贝; - 任意元素
非 Copy,元组整体为 Move,仅能移动;
- 所有元素都实现
- 无字段名:通过索引(从 0 开始)或模式匹配访问元素;
定义与初始化
元组使用 (值1, 值2, ...) 语法创建,元素之间用逗号分隔。
- 基本初始化:根据元素类型自动推导;
- 显示指定类型:手动指定元组的类型((类型1, 类型2, ...));
- 单元素元组:必须在元素后加逗号,否则会被解析为普通括号;
- 空元组:单元类型
():- 函数无返回值时,默认返回
()(可省略); - 作为 "无数据" 的占位符(如空结果)。
- 函数无返回值时,默认返回
rust
// 异构类型元组(i32, &str, bool)
let person = (25, "Alice", true);
// 同类型元组(等价于 [i32; 3])
let numbers = (10, 20, 30);
// 显式声明元组类型
let point: (f64, f64) = (3.14, 6.28);
let empty_tuple: () = (); // 空元组(类型为 (),也叫"单元类型")
let single: (i32,) = (5,); // 正确:单元素元组(类型为 (i32,))
let not_tuple = (5); // 错误:不是元组,只是 i32 类型的 5(括号仅用于优先级)
元素的访问
通过 点号 + 索引 或 模式匹配(解构) 访问元素
- 点号索引访问:从0开始,越界直接编译错误;
- 模式匹配(解构):可一次性提取多个元素,还能忽略不需要的值;
(..)表示忽略剩下的所有元素,只能出现一次;(_)表示忽略指定(一个)元素,可出现任意次;
- 可变元组(mut):若需修改元素,需给元组加
mut关键字(元组整体可变,不能单独指定某个元素可变)。
rust
let person = (25, "Alice", true, 72.5);
// 点号 + 索引访问
let age = person.0; // 25(i32 类型)
let name = person.1; // "Alice"(&str 类型)
let is_student = person.2; // true(bool 类型)
let score = person.3; // 72.5(f32类型)
// 模式匹配
let (first, ..) = person;
let (_, mid, .., score) = person;
let (.., last) = person;
let (first, .., last) = person;
// 修改元素
let mut person = (25, "Alice", true);
person.0 = 26; // 允许:修改索引 0 的元素
person.1 = "Bob"; // 允许:修改字符串切片(注意:&str 是不可变引用,这里是替换为新的 &str)
常用操作与特性
比较运算(==, !=, <, >, 等)
元组支持比较运算,但要求:
- 两个元组 长度相同;
- 对应位置的元素 类型相同且实现
PartialEqtrait(大部分基础类型都实现了)。
比较规则:按元素索引顺序逐一比较,直到找到第一个不相等的元素。
复制与移动(基于元素的 Copy trait)
元组的 "复制行为" 由其所有元素是否实现 Copy trait 决定:
- 若 所有元素都实现
Copy(如i32,bool,&str等),则元组自动实现Copy,赋值时会复制(不转移所有权); - 若 存在元素未实现
Copy(如String,Vec等),则元组不实现Copy,赋值时会转移所有权(原变量不可再使用)。
作为函数参数 / 返回值
元组是 Rust 中实现 "函数返回多个值" 的常用方式(无需定义结构体)
rust
// 函数返回元组((i32, i32):求和与乘积)
fn sum_and_product(a: i32, b: i32) -> (i32, i32) {
(a + b, a * b)
}
// 调用函数并解构返回值
let (sum, product) = sum_and_product(3, 4);
println!("sum: {}, product: {}", sum, product); // sum: 7, product: 12
// 元组作为函数参数(适合传递多个异构值)
fn print_point((x, y): (f64, f64)) {
println!("Point: ({}, {})", x, y);
}
print_point((3.14, 6.28)); // 直接传递元组,或解构传递
与数组 / 切片的区别
元组和数组([T; N])、切片(&[T])的核心区别:
| 特性 | 元组 | 数组 | 切片 |
|---|---|---|---|
| 元素类型 | 可异构(不同类型) | 必须同构 | 必须同构 |
| 长度 | 编译时固定(类型一部分) | 编译时固定 | 运行时可变 |
| 访问方式 | 点号索引 / 解构 | 方括号索引 | 方括号索引 |
| 主要用途 | 临时聚合异构值、多返回值 | 固定长度同构数据 | 动态长度同构数据视图 |