布局
首先,我们需要设计结构体的布局。一个 Vec 有三个部分:指向分配内存的指针、分配内存的大小以及已初始化元素的数量。
简单来说,这意味着我们想要这样的设计
pub struct Vec<T> {
ptr: *mut T,
cap: usize,
len: usize,
}
实际上,这确实可以编译。不幸的是,它太严格了。编译器会给我们过于严格的变型。因此,&Vec<&'static str>
不能用于预期 &Vec<&'a str>
的地方。有关变型的所有详细信息,请参阅关于所有权和生命周期的章节。
正如我们在所有权章节中看到的,标准库在拥有指向其所拥有分配内存的原始指针时使用 Unique<T>
代替 *mut T
。不过,Unique 是不稳定的,所以我们希望尽可能不使用它。
概括地说,Unique 是一个围绕原始指针的包装器,它声明
- 我们对
T
是协变的 - 我们可能拥有类型为
T
的值(这对我们这里的示例无关紧要,但请参阅关于 PhantomData 的章节,了解为什么真正的std::vec::Vec<T>
需要这样做) - 如果
T
是 Send/Sync,我们也是 Send/Sync - 我们的指针永远不会为空(因此
Option<Vec<T>>
是空指针优化的)
我们可以在稳定的 Rust 中实现上述所有要求。为此,我们将使用 NonNull<T>
(另一个围绕原始指针的包装器)来代替 Unique<T>
,它为我们提供了上述两个属性,即它对 T
是协变的,并且声明为永不为空。通过在 T
是 Send/Sync 时实现 Send/Sync,我们得到与使用 Unique<T>
相同的结果
use std::ptr::NonNull; pub struct Vec<T> { ptr: NonNull<T>, cap: usize, len: usize, } unsafe impl<T: Send> Send for Vec<T> {} unsafe impl<T: Sync> Sync for Vec<T> {} fn main() {}