HeadlinesBriefing favicon HeadlinesBriefing.com

可视化 Rust 的 Vtable:dyn Trait 在内存中是如何工作的

Hacker News •
×

我正在探索 Rust,这同时让人满意也让人眼花缭乱。迄今为止,我是从书籍和 Mara Bos 的书籍中学习的,但我产生了自己进行剖析的冲动。这些实验的初衷是将 Rust 的多态方法与 C++ 进行比较。最终,然而,我意识到,通过另一种语言来理解新语言并试图建立 1:1 类比是一条陷阱。这似乎有帮助,但说到底,我们不能把 Rust 当作带有不同语法的 C++。如果真的是这样,那就没有什么革命性的地方了。即便如此,我认为有深入探究的价值,来了解其中的原因。所以,如果你和我一样,需要知道内部究竟发生了什么,才能真正感觉自己理解了这些概念,希望这篇帖子对你有所帮助 :)顺便说一句,缩略图是真菌的照片,我们要把 Rust 的名字归功于这种真菌。Credit: gailhampshire from Cradley, Malvern, U.K, CC BY 2.0, via Wikimedia Commons. 你可以在 Git Hub 上找到所有代码和实验。

简介:核心所在

我们要实现的事情很简单。假设我们有一堆形状:圆形、方形、三角形,我们想对每一个调用 draw()。

C++ 方法 #1:虚函数 想到在 C++ 中做这件事的第一种方法是通过虚函数,这利用了运行时多态。vtable 指针驻留在对象内部,虚拟调度会自动发生。

std::vector<Shape*> shapes = { new Circle(), new Square() };

for (auto* s : shapes)s->draw();

Rust 的等价物是 dyn Trait,这就是我们最终想要理解的内容。但在此之前,让我们看看我们可以用另一种方式在 C++ 中解决这个问题。

C++ 方法 #2:CRTP 人们也可以走 CRTP(好奇的递归模板模式)的路线,这本质上是编译时多态。如果你感兴趣,Klaus Iglberger 的这篇精彩演讲是我接触这个话题的第一篇,而且我经常回去参考它。

template<typename Derived>struct Shape {void draw() {static_cast<Derived*>(this)->draw();}}; 本质上,没有 vtable 且在编译时解析,牺牲了可读性(这真的很啰嗦)。

Rust 提供了一种更直接、更简单的 CRTP 等价物,即单态化。这是我们将首先挖掘的方法,以开始构建对 Rust 有什么可提供的心理模型。

静态调度 Photo by Jiawei Zhao on Unsplash 静态调度,也被称为泛型,可以实现与 CRTP 相似的结果:编译器为每个调用它的类型生成单独的函数副本。没有运行时成本,但类型必须在编译时已知。

trait Draw {fn draw(&self) -> &str;}struct Circle;struct Square;impl Draw for Circle {fn draw(&self) -> &str {