可视化 Rust 的 Vtables:dyn Trait 内存揭秘

Visualizing Rust's Vtables: How dyn Trait Works In Memory

可视化 Rust 的 Vtables:dyn Trait 内存揭秘

作为正在深入 Rust 的开发者,我试图通过对比 C++ 来理解 Rust 的多态机制,却很快意识到这种类比是个陷阱。Rust 并非只是换了语法的 C++,其核心哲学截然不同。本文通过代码实验,深入剖析了 Rust 的静态分发与动态分发(dyn Trait)在内存层面的运作细节。你将看到 Rust 如何处理零大小类型(ZST),以及为何 &dyn Trait 是一个包含数据指针和 vtable 指针的“宽指针”。更重要的是,Rust 的 vtable 并不像 C++ 那样嵌入对象内部,而是作为外部静态数据存在,且分发方式取决于调用点而非类定义。这种设计彻底重塑了我对内存布局和类型系统的认知。

Rust 对你的思维模型做了同样的事:当你倒挂在空中时,必须要有意识地重新布线你对身体运作方式的全部感知。
  1. tialaramex

    这篇文章里有一节讲的是 Object Safety。我查了一下,这篇文章是本周写的,但“Object Safety”这个叫法对初学者来说挺让人困惑的,所以 Rust 最近开始把这一概念称为“dyn compatibility”(dyn 兼容性),因为如果一个 trait 是“dyn compatible”的,你最关键能得到的好处就是可以使用 `dyn Trait` —— https://doc.rust-lang.org/1.98.1/reference/items/traits.html...

    那个链接对规则的讲解也更全面。

    [编辑:意识到文章结尾其实解释了这一点,作者几个月前就开始写这篇文章了,很可能是在读到关于改进后的“dyn compatibility”命名之前写的。]

  2. evmar

    在我自己的探索过程中,我发现 https://cheats.rs/ 非常有帮助,尤其是它的“内存布局”部分提供了可视化展示。(我和该网站没有任何关联,只是一个快乐的读者!)

  3. Panzerschrek

    > 一个 trait 必须遵循所谓的 object safety 规则才能被用作 trait object

    在我看来,这是 Rust 的一个重大设计缺陷。它试图让 trait 承担动态多态的功能,哪怕这并不完全契合。C++ 更诚实,它有两套独立的机制:静态多态(templates)和动态多态(inheritance)。

  4. Panzerschrek

    我曾经在 Rust 中遇到过一个棘手的 bug,涉及 fat pointers(包含虚函数表)。两个这样的指针可能并不相同,即使它们指向同一个对象,因为(出于某种原因)编译器可能会创建两份(甚至更多)虚函数表的副本,并在不同地方使用它们。

  5. returningfory2

    非常棒。作为后续,如果能逆向分析 vtable 本身的结构也会很有趣。我猜它应该是一个指向方法实现的指针列表?

同日更多故事

2026-09-05