Vec
是 Rust 标准库中提供的集合之一。
Vec
一种连续的可增长数组类型,所有存储数据类型必须一致,写作 Vec<T>
,发音为 “vector”。
原型如下:
1 | pub struct Vec<T, A = Global> |
这篇文章只会介绍几个基础用法,用以入门,详细内容请看官方文档 Vec in std::vec - Rust。
1. 新建 Vec
新建 Vec
主要有两种方式:
1. 调用函数 Vec::new()
创建一个空的 Vec
;
1 | pub const fn new() -> Vec<T, Global> |
在元素被推入 vector 之前,vector 不会分配。
2. 调用宏 vec!
从现有元素创建一个 Vec
。
下面从代码示例了解如何新建 Vec
:
1 | let mut v0 = Vec::new(); // 新建一个空的 Vec,其中的类型 T 将在插入第一个元素时由 Rust 推断,要注意编译前必须有插入操作,否则会编译失败,Rust 需要在编译前就知道其中元素的类型。 |
请注意,上面的几个写法均要加关键字 mut
,否则创建出的 Vec 将是不可变的,你不能插入或者修改、删除元素。
2. 更新 Vec
2.1. 在最后插入一个元素 push
1 | pub fn push(&mut self, value: T) |
下面看示例代码:
1 |
|
2.2. pop
删除最后一个元素并返回其值
从 vector 中移除最后一个元素并返回它,如果为空则返回 None
。
1 | pub fn pop(&mut self) -> Option<T> |
下面看示例代码:
1 |
|
这里要注意的是,当 vector 为空的时候,调用 pop()
并不会报错,而是会返回 None
,所以这里判断返回值的时候应该使用 Option<T>
,即 Some(T)
和 None
。
3. 读取单个元素
读取单个元素主要有两种方法:
1. 通过 []
运算符访问;
2. 通过函数 get()
访问。
下面的代码中使用了分别使用了上述两种方法来获取 v
中索引为 2
的元素:
1 |
|
这两种方式是有所不同的。使用 []
则和大多数编程语言类似,如果索引非法,则会使程序崩溃,Rust 中则是 Panic !但是如果使用的是 get()
函数,返回的是 Option<&T>
,即当索引非法的时候,会返回 None
,也就需要额外的分支判断。
还有一个区别是,你可以通过 []
访问的方式来修改 vector 中的值(前提是 Vec 声明时为 mut
),但是 get()
不可以,因为其返回的 Option<&T>
是不可变引用。若想使用此方式修改 vector,使用函数 get_mut()
其返回的是 Option<&mut T>
。
下面是 get_mut()
的示例代码:
1 |
|
4. 遍历 Vec
Rust 中遍历 vector 有两种常用的方法:
一种是通过索引,这和大部分其他编程语言一样;
另一种是通过 for
来获取 vector 中的每一个元素的可变或不可变引用。
4.1. 通过索引访问 Vec
如果你想通过索引访问整个 vector,则需要知道整个 vector 的长度,Rust 中的 Vec
提供了函数 len()
。
当然通过索引,你也可以更方便的访问其中某一段的元素。
1 | fn main() { |
4.2. 通过引用来访问 Vec
如果想要依次访问 vector 中的每一个元素,我们可以使用引用来遍历其所有的元素,而无需通过索引一个一个来。
1 | fn main() { |
这里要注意 *i += 1;
中,在 i
前面加了 *
解引用,这是因为 i
是一个引用(可变引用),我们可以通过引用访问其值,但是若要修改就需要解引用。
5. 扩展:一个常见的错误
下面看一段代码:
1 | fn main() { |
虽然这段代码看起来没什么问题,但是编译时还是会报错如下:
1 | error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable |
我在 Rust — 所有权(Ownership) 这篇文章中,有一个关于引用的总结如下:
- 在同一时间,对于一个值,你最多可以有一个可变引用,可以有任意数量的不可变引用;
- 引用必须始终有效;
- 如果你有一个(或多个)不可变引用,那么从这个不可变引用的声明开始,直到这个不可变引用的最后一次使用,其间不可以引入(声明)可变引用。当引入(声明)了可变引用以后,这个不可变引用将不再可用;
- 如果你有一个可变引用,那么从这个可变引用的声明开始,直到这个可变引用的最后一次使用,其间不可以引入(声明)不可变引用。当引入(声明)了不可变引用以后,这个可变引用将不再可用。
现在再看上面的代码,报错信息显示在 first = &v[0];
处发生了不可变引用,而 v.push(6);
处发生了可变引用,最后打印 first
的时候使用了不可变引用,
我们知道三处错误所说的引用目标,只能是 v[0]
,但是为什么 v.push(6);
在 v
的末端插入元素会影响到 v[0]
呢?这跟 Rust 中 Vec
的工作方式有关:在 vector 的结尾增加新元素时,若没有足够空间将所有所有元素依次相邻存放,可能会要求分配新内存并将旧的元素拷贝到新的空间中。这时,第一个元素 v[0]
的引用就指向了被释放的内存,所以 push()
操作会创建一个 v[0]
的可变引用(虽然不一定用得到)来改变其所引用的位置。
那我们把 let first = &v[0];
改为 let mut first = &v[0];
会怎样呢?
就像下面的代码这样,这样就不存在可变引用与不可变引用共存的问题了。
1 | fn main() { |
这段代码也是错误的,结合上一段错误代码的分析,再看我们之前的总结第一条:
- 在同一时间,对于一个值,你最多可以有一个可变引用,可以有任意数量的不可变引用;
我们的 let first = &mut v[0];
和 v.push(6);
都会创建一个 v[0]
的可变引用,这是非法的!