跳转到内容

Rust 数组

数组(array)是一组长度固定、类型相同的值。关键词是“长度固定”:数组一旦创建,就不能再变长或变短。如果需要能改变大小的列表,那是 vector 的工作。

fn main() {
let numbers: [i32; 5] = [1, 2, 3, 4, 5];
println!("{:?}", numbers);
}

类型 [i32; 5] 读作“由 5 个 i32 值组成的数组”。长度是类型的一部分:[i32; 5] 和 [i32; 3] 是两种不同的类型。

▶ 动手试一试

也可以不写类型,只写值,让 Rust 自己推断:

let numbers = [1, 2, 3, 4, 5];

想用同一个值把数组填满 N 次,可以用这种简写:

fn main() {
let zeros = [0; 5]; // [0, 0, 0, 0, 0]
println!("{:?}", zeros);
}
▶ 动手试一试

数组的索引从 0 开始,用方括号来访问:

fn main() {
let fruits = ["苹果", "香蕉", "樱桃"];
println!("{}", fruits[0]); // 苹果
println!("{}", fruits[2]); // 樱桃
println!("元素个数:{}", fruits.len());
}
▶ 动手试一试

如果访问一个不存在的索引,Rust 会直接停止程序,而不是悄悄读出一堆垃圾数据:

fn get_fruit(fruits: [&str; 3], index: usize) -> &str {
fruits[index]
}
fn main() {
let fruits = ["苹果", "香蕉", "樱桃"];
println!("{}", get_fruit(fruits, 10)); // 运行时会 panic
}
thread 'main' (12345) panicked at src/main.rs:2:5:
index out of bounds: the len is 3 but the index is 10

这条 panic 信息的意思是:“索引越界:长度是 3,但索引是 10”。

fn main() {
let numbers = [10, 20, 30];
for n in numbers {
println!("{n}");
}
}
▶ 动手试一试

数组里也可以放数组。这样就得到一个有行有列的网格,比如棋盘或者一张小表格。下面 grid 的类型是 [[i32; 3]; 2],读作“2 行,每行是一个由 3 个 i32 值组成的数组”。

fn main() {
// 2 行 3 列
let grid = [
[1, 2, 3],
[4, 5, 6],
];
println!("第 1 行第 2 列:{}", grid[1][2]);
let mut total = 0;
for row in grid {
println!("{:?}", row);
for value in row {
total += value;
}
}
println!("总和:{total}");
}

输出:

第 1 行第 2 列:6
[1, 2, 3]
[4, 5, 6]
总和:21
▶ 动手试一试

grid[1][2] 的意思是“先取第 1 行,再取这一行里的第 2 列”。行和列都从 0 开始数,所以这里其实是第二行的第三个值。要访问每一个值,就用一个循环遍历各行,再在里面套一个循环遍历这一行的值。

每一行的长度都必须相同,因为行的长度是类型的一部分。如果遍历时还需要知道每个位置的索引,可以用 .iter().enumerate() 同时拿到索引和值,for 循环那一课里有介绍。

如果大小固定、事先就知道(比如一周七天),就用数组。如果需要添加或删除元素,就用 Vec,而实际写代码时大多数情况都是这样。在日常的 Rust 代码里,数组远没有 vector 常见。

创建数组 [4, 8, 15, 16, 23, 42]。把所有值加起来,然后打印总和,以及保留两位小数的平均值。提示:用 for 循环,维护一个不断累加的 total,做除法之前用 as 把两个数都转成 f64。

查看答案
fn main() {
let numbers = [4, 8, 15, 16, 23, 42];
let mut total = 0;
for n in numbers {
total += n;
}
let average = total as f64 / numbers.len() as f64;
println!("总和:{total}");
println!("平均值:{average:.2}");
}

输出:

总和:108
平均值:18.00
▶ 动手试一试

如果不写 as f64,total / numbers.len() 就是整数除法,而且 Rust 还会报错,说 total(i32)和 len()(usize)是不同的类型。

这里有一周的气温:[18, 25, 12, 31, 22, 27, 19]。找出最高的那个并打印出来。提示:先写 let mut hottest = temps[0];,然后循环,每遇到更大的值就替换它。

查看答案
fn main() {
let temps = [18, 25, 12, 31, 22, 27, 19];
let mut hottest = temps[0];
for t in temps {
if t > hottest {
hottest = t;
}
}
println!("最热的一天:{hottest}");
}

输出:

最热的一天:31
▶ 动手试一试

从 temps[0] 开始而不是从 0 开始很重要:如果每天的气温都在零度以下,从 0 开始就会得到错误的答案。

  • 数组的长度固定,而且长度是类型的一部分:[i32; 5]。
  • 用 array[index] 访问元素,索引从 0 开始。
  • 越界访问会在运行时 panic,而不会导致未定义行为。
  • 数组里可以放数组:[[i32; 3]; 2] 是一个网格,用 grid[行][列] 来读取。
  • 需要改变大小时,改用 Vec。

小测验

1. 关于 Rust 数组的长度,哪种说法是对的?

2. 在运行时访问越界的数组索引会发生什么?

3. let zeros = [0; 5]; 创建的是什么?

得分: 0 / 3