切片(Slices)

让我们回到 Vec 的内存布局:

#![allow(unused)]
fn main() {
let mut numbers = Vec::with_capacity(3);
numbers.push(1);
numbers.push(2);
}
      +---------+--------+----------+
Stack | pointer | length | capacity | 
      |  |      |   2    |    3     |
      +--|------+--------+----------+
         |
         |
         v
       +---+---+---+
Heap:  | 1 | 2 | ? |
       +---+---+---+

我们之前提到过 String 实际上是伪装过的 Vec<u8>。这种相似性应该促使你发问:“对于 Vec,是否存在类似于 &str 的东西?”

&[T]

[T] 是类型为 T 的连续元素序列的切片。它最常以引用形式 &[T] 出现。

有多种方式可以从 Vec 创建切片引用:

#![allow(unused)]
fn main() {
let numbers = vec![1, 2, 3];
// 通过索引语法
let slice: &[i32] = &numbers[..];
// 通过方法
let slice: &[i32] = numbers.as_slice();
// 或仅针对元素的子集
let slice: &[i32] = &numbers[1..];
}

Vec 使用 [T] 作为目标类型实现了 Deref 特性,因此由于解引用强制转换,你可以在 Vec 直接使用切片方法:

#![allow(unused)]
fn main() {
let numbers = vec![1, 2, 3];
// 惊喜!“iter”并不是“Vec”上的方法!
// 它实际上是 “&[T]” 上的方法,但由于解引用强制,
// 你可以在 “Vec” 上直接调用它。
let sum: i32 = numbers.iter().sum();
}

内存布局

&[T] 是一个宽指针,就像 &str 一样。它由指向切片第一个元素的指针和切片长度组成。

如果你有一个包含三个元素的 Vec

#![allow(unused)]
fn main() {
let numbers = vec![1, 2, 3];
}

然后创建一个切片引用:

#![allow(unused)]
fn main() {
let slice: &[i32] = &numbers[1..];
}

你会得到这样的内存布局:

                  numbers                          slice
      +---------+--------+----------+      +---------+--------+
Stack | pointer | length | capacity |      | pointer | length |
      |    |    |   3    |    4     |      |    |    |   2    |
      +----|----+--------+----------+      +----|----+--------+
           |                                    |  
           |                                    |
           v                                    | 
         +---+---+---+---+                      |
Heap:    | 1 | 2 | 3 | ? |                      |
         +---+---+---+---+                      |
               ^                                |
               |                                |
               +--------------------------------+

&Vec<T>&[T] 的比较

当你需要将 Vec 的不可变引用传递给函数时,优选 &[T] 而非 &Vec<T>。这使得函数能够接受任何形式的切片,而不一定是由 Vec 支持的。

例如,你可以传递 Vec 中元素的子集。 但不仅如此,你还可以传递数组的切片

#![allow(unused)]
fn main() {
let array = [1, 2, 3];
let slice: &[i32] = &array;
}

数组切片和 Vec 切片是相同类型:它们都是指向连续元素序列的宽指针。对于数组来说,指针指向栈而非堆,但在使用切片时这一点并不重要。