5.1 数组与元组
在 Rust 中,数组和元组是两种最基本的复合数据类型。它们都用于将多个值组合成一个单一的类型,但它们在用途、特性和访问方式上有着显著的区别。理解这两种类型是掌握 Rust 数据结构的基础。
数组 (Array)
数组是一种固定长度、同类型元素的集合。一旦声明,数组的大小就不能改变。数组中的元素存储在栈(Stack)上,这使得访问速度非常快。
声明与初始化
数组的类型签名为 [T; N],其中 T 是元素的类型,N 是编译时已知的数组长度(一个非负整数常量)。
fn main() {
// 方式1: 显式声明类型和长度,并列出所有元素
let months: [&str; 12] = [
"January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December"
];
// 方式2: 类型推断
let numbers = [1, 2, 3, 4, 5];
// 方式3: 使用重复初始化语法 [初始值; 长度]
// 创建一个包含5个0的数组
let zeros: [i32; 5] = [0; 5]; // 等价于 [0, 0, 0, 0, 0]
// 创建一个包含3个"hello"的数组
let greetings = ["hello"; 3]; // 等价于 ["hello", "hello", "hello"]
}
访问元素
数组元素通过索引访问,索引从 0 开始。
fn main() {
let numbers = [10, 20, 30, 40, 50];
let first = numbers[0]; // 10
let third = numbers[2]; // 30
println!("First: {}, Third: {}", first, third); // 输出: First: 10, Third: 30
}
越界访问
如果尝试访问超出数组长度的索引,程序会在运行时发生 panic(崩溃)。这是 Rust 内存安全性的一个体现,它防止了访问无效内存。
fn main() {
let numbers = [1, 2, 3];
// 以下代码会导致 panic: index out of bounds
// let element = numbers[10];
}
数组的遍历
通常使用 for 循环来遍历数组。
fn main() {
let primes = [2, 3, 5, 7, 11];
for prime in primes {
println!("Prime: {}", prime);
}
// 如果需要索引,可以使用 .iter().enumerate()
for (index, value) in primes.iter().enumerate() {
println!("Index {}: {}", index, value);
}
}
元组 (Tuple)
元组是一种固定长度、可以包含不同类型元素的集合。元组也存储在栈上。它常用于从一个函数返回多个值,或者将一组相关的、但类型不同的数据组合在一起。
声明与初始化
元组的类型签名为 (T1, T2, T3, ...),其中 T1, T2, T3 等是每个元素的类型。
fn main() {
// 方式1: 显式声明类型
let person: (&str, i32, f64) = ("Alice", 30, 1.75);
// 方式2: 类型推断
let point = (10, 20.5, 'A');
// 可以创建空元组,称为 unit 类型,表示“没有值”
let empty: () = ();
}
访问元素
元组有两种访问方式:
- 使用点号
.加索引:tuple.0,tuple.1,tuple.2等。 - 解构 (Destructuring):将元组拆解为独立的变量。
fn main() {
let person = ("Bob", 25, true);
// 方式1: 点号索引
let name = person.0; // "Bob"
let age = person.1; // 25
let is_student = person.2; // true
println!("Name: {}, Age: {}, Student: {}", name, age, is_student);
// 方式2: 解构
let (name, age, is_student) = person;
println!("Name: {}, Age: {}, Student: {}", name, age, is_student);
// 如果不需要某些元素,可以使用 _ 忽略
let (name, _, _) = person;
println!("Name: {}", name);
}
元组与数组的比较
| 特性 | 数组 | 元组 |
|---|---|---|
| 元素类型 | 必须相同 | 可以不同 |
| 长度 | 固定,编译时已知 | 固定,编译时已知 |
| 访问方式 | 索引 [index] | 点号 .index 或解构 |
| 内存位置 | 栈 | 栈 |
| 典型用途 | 同类型元素的集合,如数字列表 | 不同类型数据的组合,如坐标 (x, y),或函数返回值 |
实战示例:使用元组返回多个值
这是元组最常用的场景之一。
fn calculate_length(s: String) -> (String, usize) {
let length = s.len();
// 返回元组,包含原始字符串和它的长度
(s, length)
}
fn main() {
let my_string = String::from("Hello, Rust!");
// 解构函数返回的元组
let (returned_string, len) = calculate_length(my_string);
println!("The string '{}' has length {}.", returned_string, len);
// 输出: The string 'Hello, Rust!' has length 12.
}
总结
- 数组
[T; N]用于存储固定数量、相同类型的元素,通过索引快速访问。 - 元组
(T1, T2, ...)用于组合固定数量、可能不同类型的元素,通过点号索引或解构访问。 - 两者都是栈上分配的,长度固定,是 Rust 中构建更复杂数据结构的基础。理解它们各自的适用场景,能帮助你写出更清晰、更高效的 Rust 代码。
