pointers - 什么是 "fat pointer"?

标签 pointers rust

我已经在几个上下文中阅读过“胖指针”这个术语,但我不确定它的确切含义以及它何时在 Rust 中使用。指针似乎是普通指针的两倍,但我不明白为什么。它似乎也与特征对象有关。

最佳答案

术语“胖指针”用于指代动态大小类型(DST)的引用和原始指针——切片或特征对象。 胖指针包含一个指针和一些使 DST “完整”的信息(例如长度)。
Rust 中最常用的类型不是 DST,而是在编译时具有固定的大小。这些类型实现 the Sized trait .甚至管理动态大小的堆缓冲区的类型(如 Vec<T> )也是 Sized ,因为编译器知道确切的字节数 a Vec<T>实例将占用堆栈。 Rust 目前有四种不同类型的 DST。

切片( [T]str )
类型[T] (对于任何 T )是动态大小的(特殊的“字符串切片”类型 str 也是如此)。这就是为什么您通常只将其视为 &[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,对它的引用是它的两倍。 在切片的情况下,“完成” DST 的附加数据只是长度。 所以可以说&[u32]的表示是这样的:
struct SliceRef { 
    ptr: *const u32, 
    len: usize,
}

特征对象 ( dyn Trait )
当使用 trait 作为 trait 对象(即类型删除、动态分派(dispatch))时,这些 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是一个特征对象,因此是动态大小的。因此,&dyn Animal是 16 字节大。
在 trait 对象的情况下,完成 DST 的附加数据是指向 vtable(vptr)的指针。 我无法在这里完全解释 vtables 和 vptrs 的概念,但它们用于在这个虚拟调度上下文中调用正确的方法实现。 vtable 是一条静态数据,基本上只包含每个方法的函数指针。这样,对特征对象的引用基本上表示为:
struct TraitObjectRef {
    data_ptr: *const (),
    vptr: *const (),
}
(这与 C++ 不同,其中抽象类的 vptr 存储在对象中。两种方法都有优点和缺点。)

自定义 DST
实际上可以通过在最后一个字段是 DST 的结构中创建自己的 DST。不过,这种情况相当罕见。一个突出的例子是 std::path::Path .
对自定义 DST 的引用或指针也是胖指针。附加数据取决于结构内的 DST 类型。

异常(exception):外部类型
RFC 1861 , extern type功能介绍。 Extern 类型也是 DST,但指向它们的指针不是胖指针。或者更准确地说,正如 RFC 所说:

In Rust, pointers to DSTs carry metadata about the object being pointed to. For strings and slices this is the length of the buffer, for trait objects this is the object's vtable. For extern types the metadata is simply (). This means that a pointer to an extern type has the same size as a usize (ie. it is not a "fat pointer").


但是,如果您不与 C 接口(interface)交互,您可能永远不必处理这些外部类型。

上面,我们已经看到了不可变引用的大小。胖指针对可变引用、不可变原始指针和可变原始指针的工作方式相同:
size_of::<&[u32]>()       = 16
size_of::<&mut [u32]>()   = 16
size_of::<*const [u32]>() = 16
size_of::<*mut [u32]>()   = 16

关于pointers - 什么是 "fat pointer"?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66596003/

相关文章:

reference - 有什么方法可以返回对在函数中创建的变量的引用?

python - 如何将指针传递给指向 python 结构的指针数组

c++ - 是否应该在 memset 之前对结构进行类型转换

C - 传递 message_t 参数

rust - 我可以用来允许访问集合的中间元素的最一般特征是什么?

python - Python 的键盘中断不会中止 Rust 功能(PyO3)

pointers - 引用 int 的盒子和引用 rust 中的 int 之间的区别?

带指针的 c strcat

rust - 为 Diesel "insertable"结构指定生命周期参数有什么好处

rust - 为什么 ~/.cargo 目录这么大?