我已经在几个上下文中读到过“胖指针”这个术语,但是我不确定它到底是什么意思,以及在 Rust 中什么时候使用它。指针似乎是普通指针的两倍大,但我不明白为什么。它似乎还与 trait 对象有关。
术语“胖指针”用于指向 动态调整大小的类型(DST)-片或特征对象的引用和原始指针。一个胖指针包含一个指针加上一些使 DST“完整”的信息(例如长度)。
Rust 中最常用的类型是 没有 DST,但在编译时具有已知的固定大小。这些类型实现 Sized特性。即使是管理动态大小堆缓冲区(如 Vec<T>)的类型也是 Sized,因为编译器知道 Vec<T>实例将在堆栈上占用的确切字节数。目前 Rust 中有四种不同类型的 DST。
Sized
Vec<T>
[T]
str
类型 [T](对于任何 T)都是动态调整大小的(特殊的“字符串片”类型 str也是如此)。这就是为什么你通常只看到它作为 &[T]或 &mut [T],即后面的参考。这个引用是一个所谓的“胖指针”。我们来看看:
T
&[T]
&mut [T]
dbg!(size_of::<&u32>()); dbg!(size_of::<&[u32; 2]>()); dbg!(size_of::<&[u32]>());
这个打印(经过一些清理) :
size_of::<&u32>() = 8 size_of::<&[u32; 2]>() = 8 size_of::<&[u32]>() = 16
因此,我们可以看到像 u32这样的普通类型的引用大小为8字节,对数组 [u32; 2]的引用也是如此。这两种类型不是 DST。但是因为 [u32]是 DST,所以对它的引用是它的两倍大。所以可以说 &[u32]的表示是这样的:
u32
[u32; 2]
[u32]
&[u32]
struct SliceRef { ptr: *const u32, len: usize, }
dyn Trait
当使用 trait 作为特性对象(例如类型擦除,动态分派)时,这些特性对象是 DST。例如:
trait Animal { fn speak(&self); } struct Cat; impl Animal for Cat { fn speak(&self) { println!("meow"); } } dbg!(size_of::<&Cat>()); dbg!(size_of::<&dyn Animal>());
size_of::<&Cat>() = 8 size_of::<&dyn Animal>() = 16
同样,&Cat只有8个字节大,因为 Cat是普通类型。但是 dyn Animal是一个 trait 对象,因此可以动态调整大小。因此,&dyn Animal大小为16字节。
&Cat
Cat
dyn Animal
&dyn Animal
对于 trait 对象,完成 DST 的附加数据是指向 vtable (vptr)的指针。这里我不能完全解释 vtables 和 vptrs 的概念,但是它们用于调用这个虚拟分派上下文中的正确方法实现。Vtable 是一个静态数据块,基本上只包含每个方法的函数指针。因此,对 trait 对象的引用基本上表示为:
struct TraitObjectRef { data_ptr: *const (), vptr: *const (), }
(这不同于 C + + ,在 C + + 中,抽象类的 vptr 存储在对象中。这两种方法各有优缺点。)
实际上可以通过使用一个 struct 来创建自己的 DST,其中最后一个字段是 DST。不过这种情况相当罕见。一个突出的例子是 std::path::Path。
std::path::Path
指向自定义 DST 的引用或指针也是胖指针。额外的数据取决于结构中 DST 的类型。
在 RFC 1861中,引入了 extern type特性。外部类型也是 DST,但指向它们的指针是 没有胖指针。或者更确切地说,正如 RFC 所言:
extern type
在 Rust 中,指向 DST 的指针携带关于所指向对象的元数据。对于字符串和切片,这是缓冲区的长度,对于 trait 对象,这是对象的 vtable。对于外部类型,元数据只是 ()。这意味着指向外部类型的指针的大小与 usize相同(即。它不是一个“肥指针”)。
()
usize
但是如果您没有与 C 接口进行交互,那么您可能永远不需要处理这些外部类型。
在上面,我们已经看到了不可变引用的大小。胖指针对可变引用、不可变原始指针和可变原始指针的工作原理是一样的:
size_of::<&[u32]>() = 16 size_of::<&mut [u32]>() = 16 size_of::<*const [u32]>() = 16 size_of::<*mut [u32]>() = 16