Rust中的胖指针:深入理解fat pointer

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

“胖”指针背后隐藏着什么?深入解析Rust中的fat pointer

\n\n

想象一下,你去图书馆借书。图书管理员给你一张小纸条,上面写着书架和层架的编号——这就是普通指针。但是,如果你不知道书的大小呢?或者它是一套三卷本?你就需要第二张纸条:“第1卷,共3卷”或“长度:450页”。

\n\n

在Rust中,这种“双重”指针被称为fat pointer(胖指针)。这并非什么魔法,而仅仅是一对数字:数据地址及其元信息(通常是长度或指向虚方法表的指针)。让我们通过实际的代码示例来理解它的用途和工作原理。

\n\n

普通指针:瘦小且快速

\n\n

从基础开始。Rust中有简单的指针——&T(引用)或*const T(裸指针)。它们恰好占用8个字节(在64位系统上),并且只存储内存地址。

\n\n
fn main() {\n    let x: i32 = 42;\n    let ptr: &i32 = &x; // 瘦指针——8字节\n    println!("引用的大小: {} 字节", std::mem::size_of_val(&ptr));\n}\n
\n\n

输出:引用的大小: 8 字节。一切都在预料之中:编译器知道i32是4个字节,因此不需要额外信息。但是,如果数据的大小事先未知呢?

\n\n

第一个胖指针:切片 (&[T])

\n\n

切片是数组或向量的“窗口”。但要使用它,不仅需要知道起始位置,还需要知道元素的数量。Rust将其存储为一对(ptr, len)——这就是fat pointer。

\n\n
fn main() {\n    let arr = [1, 2, 3, 4, 5];\n    let slice: &[i32] = &arr[1..4]; // 包含3个元素的切片\n\n    println!("切片的大小: {} 字节", std::mem::size_of_val(&slice));\n    println!("元素: {:?}", slice);\n}\n
\n\n

输出:切片的大小: 16 字节(8字节指针 + 8字节长度)。如果我们只传递地址,我们将不知道在哪里停止。这就是为什么切片是fat pointer的经典示例。

\n\n

顺便提一下,字符串&str的工作原理完全相同:它是一个fat pointer (*const u8, usize)。正因如此,&str占用16个字节,而&String只占用8个字节(因为String自身在内部存储了长度)。

\n\n

第二个胖指针:trait对象 (&dyn Trait)

\n\n

Rust通过trait对象支持动态分发。当你编写&dyn MyTrait时,编译器不知道将传递哪个具体类型。它需要一个虚方法表(vtable)来调用正确的函数。

\n\n
trait Animal {\n    fn speak(&self);\n}\n\nstruct Dog;\nstruct Cat;\n\nimpl Animal for Dog {\n    fn speak(&self) { println!("汪!"); }\n}\n\nimpl Animal for Cat {\n    fn speak(&self) { println!("喵!"); }\n}\n\nfn main() {\n    let dog = Dog;\n    let cat = Cat;\n\n    let animals: [&dyn Animal; 2] = [&dog, &cat];\n\n    println!("&dyn Animal 的大小: {} 字节", std::mem::size_of::<&dyn Animal>());\n\n    for animal in &animals {\n        animal.speak();\n    }\n}\n
\n\n

输出:&dyn Animal 的大小: 16 字节。这16个字节再次构成了fat pointer:前8个字节是数据地址(Dog或Cat),后8个字节是vtable(方法表)的地址。

\n\n

fat pointer如何影响性能?

\n\n

胖指针是一种权衡。一方面,它们携带更多信息,从而能够处理动态类型。另一方面:

\n\n
    \n
  • 内存: fat pointer占用16个字节而不是8个。这通常不关键,但在大型集合中可能会产生影响。
  • \n
  • 速度: 通过vtable访问(对于trait对象)比直接方法调用(单态化)稍慢。差异

博客

书籍推荐