向量 (Vectors)

数组的优点也是它的弱点:大小必须在编译期就已知。 如果你尝试创建一个大小只有运行时才能知道的数组,会得到编译错误:

let n = 10;
let numbers: [u32; n];
error[E0435]: attempt to use a non-constant value in a constant
 --> src/main.rs:3:20
  |
2 | let n = 10;
3 | let numbers: [u32; n];
  |                    ^ non-constant value

数组无法满足我们的工单管理系统——我们在编译期不知道要存多少工单。 这就是 Vec 出场的时候。

Vec

Vec 是标准库提供的可增长数组类型。
你可以用 Vec::new 函数创建一个空数组:

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

然后用 push 方法把元素压入向量:

numbers.push(1);
numbers.push(2);
numbers.push(3);

新值会被加到向量末尾。
如果你在创建时就知道值,也可以用 vec! 宏创建一个已初始化的向量:

let numbers = vec![1, 2, 3];

访问元素 (Accessing elements)

访问元素的语法跟数组相同:

let numbers = vec![1, 2, 3];
let first = numbers[0];
let second = numbers[1];
let third = numbers[2];

索引必须是 usize 类型。
也可以用 get 方法,返回 Option<&T>

let numbers = vec![1, 2, 3];
assert_eq!(numbers.get(0), Some(&1));
// 越界时返回 `None`,
// 而不是 panic。
assert_eq!(numbers.get(3), None);

访问会做边界检查,跟数组的元素访问一样,复杂度是 O(1)。

内存布局 (Memory layout)

Vec 是堆分配 (heap-allocated) 的数据结构。
你创建 Vec 时,它在堆上分配内存来存储元素。

如果你运行下面的代码:

let mut numbers = Vec::with_capacity(3);
numbers.push(1);
numbers.push(2);

会得到这样的内存布局:

      +---------+--------+----------+
Stack | pointer | length | capacity | 
      |  |      |   2    |    3     |
      +--|------+--------+----------+
         |
         |
         v
       +---+---+---+
Heap:  | 1 | 2 | ? |
       +---+---+---+

Vec 跟踪三件事:

  • 指向你在堆上预留区域的指针 (pointer)
  • 向量的长度 (length),即向量中有多少元素。
  • 向量的容量 (capacity),即在堆上预留的空间能装多少元素。

这种布局看起来应当很眼熟:和 String 完全一样!
这不是巧合:String 在底层就是定义为字节向量 Vec<u8>

pub struct String {
    vec: Vec<u8>,
}

原文链接:英文原文