跳转到内容

Rust vector(Vec)

vector(Vec)和数组很像,但它可以在运行时变长或变短。在日常的 Rust 代码里,需要一个列表时,你用到 Vec 的次数会远远多于固定长度的数组。

fn main() {
let numbers: Vec<i32> = vec![1, 2, 3];
println!("{:?}", numbers);
}

vec![] 是一个宏(注意那个 !),它用一串值构建出一个 Vec,写法和数组字面量差不多。

▶ 动手试一试

也可以先创建一个空的,再一点点往里加:

let mut numbers: Vec<i32> = Vec::new();

用 .push() 把元素加到末尾。因为要修改 vector,所以它必须是 mut 的:

fn main() {
let mut numbers = Vec::new();
numbers.push(1);
numbers.push(2);
numbers.push(3);
println!("{:?}", numbers);
}

输出:

[1, 2, 3]
▶ 动手试一试

和数组一样,用方括号:

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

想要更安全、索引不对时也不会 panic 的查找方式,就用 .get(),它返回的是一个 Option(详见 Option 那一课):

fn main() {
let fruits = vec!["苹果", "香蕉", "樱桃"];
match fruits.get(10) {
Some(fruit) => println!("找到了:{fruit}"),
None => println!("这个索引上没有水果"),
}
}
▶ 动手试一试
fn main() {
let mut numbers = vec![1, 2, 3, 4, 5];
numbers.pop(); // 删除最后一个元素 -> [1, 2, 3, 4]
numbers.remove(0); // 按索引删除 -> [2, 3, 4]
println!("{:?}", numbers);
}
▶ 动手试一试
fn main() {
let numbers = vec![10, 20, 30];
for n in &numbers {
println!("{n}");
}
}
▶ 动手试一试

注意 numbers 前面的 &:用 for n in &numbers 遍历时,每个元素都是被借用的,而不是拿走整个 vector 的所有权。如果去掉 &,vector 就会被移动到循环里,之后就不能再用 numbers 了。几乎所有时候,你想要的都是借用。

你可能注意到了,类型写的是 Vec<i32>。<i32> 这部分表示“一个装着 i32 值的 vector”。这种尖括号写法叫作泛型(generics),泛型那一课会正式讲到它。现在只要把 Vec<T> 读作“一个存放 T 类型值的 vector”就行。

从一个空的 vector 开始。遍历 1 到 10,只把偶数 push 进去。然后打印这个 vector,以及它有多少个元素。提示:当 n % 2 == 0 时,数字 n 就是偶数。

查看答案
fn main() {
let mut evens = Vec::new();
for n in 1..=10 {
if n % 2 == 0 {
evens.push(n);
}
}
println!("{:?}", evens);
println!("找到了 {} 个偶数", evens.len());
}

输出:

[2, 4, 6, 8, 10]
找到了 5 个偶数
▶ 动手试一试

注意我们一直没写类型。Rust 看到了 push(n) 的调用,就推断出 evens 是一个 Vec<i32>。

往 vector 里放三个盘子:先 "蓝色",再 "绿色",最后 "红色"。用 .pop() 从最上面拿走两个盘子,每拿一个就打印一次。最后打印还剩下什么。提示:.pop() 返回的是 Option,所以要用 {:?} 打印。

查看答案
fn main() {
let mut plates = Vec::new();
plates.push("蓝色");
plates.push("绿色");
plates.push("红色");
println!("拿走了 {:?}", plates.pop());
println!("拿走了 {:?}", plates.pop());
println!("还剩:{:?}", plates);
}

输出:

拿走了 Some("红色")
拿走了 Some("绿色")
还剩:["蓝色"]
▶ 动手试一试

pop() 总是拿走你最后放进去的那个元素,就像从一摞盘子的最上面拿一个。它返回的是 Some(...) 而不是值本身,因为 vector 有可能是空的。那种情况下你会得到 None,而不是程序崩溃。

  • Vec<T> 是可以增长的列表,用 vec![...] 或 Vec::new() 创建。
  • .push() 加到末尾,.pop() 删除最后一个元素,.remove(i) 按索引删除。
  • 索引不对时,vec[i] 会 panic,而 .get(i) 返回一个 Option,不会 panic。
  • 用 for item in &vec 遍历,借用每个元素,而不是消耗掉整个 vector。

小测验

1. 用一个不存在的索引访问 Vec(比如 numbers[10])会发生什么?

2. 哪个方法可以按索引查找元素,又不用担心 panic?

3. for n in &numbers 里的 & 有什么作用?

得分: 0 / 3