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