HeadlinesBriefing favicon HeadlinesBriefing.com

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

Hacker News •
×

I’m venturing into Rust and it’s both satisfying and mind-boggling at the same time. So far I’ve been learning from the book and Mara Bos’ book, but I got the itch to do some dissecting myself. My initial goal of these experiments was to compare Rust’s approach to polymorphism with C++’s.

Ultimately, however, as I’ve come to realize, it’s a bit of a trap when trying to understand a new language through another one to try to draw 1:1 parallels. It might seem like it helps, but at the end of the day, we can’t treat Rust as C++ with different syntax. If that were the case, there’d be nothing revolutionary about it.

That said, I believe there is merit in poking around and coming to understand the why. So, if you’re like me and need to know what exactly is happening in memory, in order to feel like you truly understand the concepts, hopefully you’ll find this post useful :)By the way, the thumbnail image is a photo of the rust fungus, to which we owe Rust’s name. Credit: gailhampshire from Cradley, Malvern, U.

K, CC BY 2.0, via Wikimedia Commons. You can find all the code and experiments on Git Hub. Introduction: The Crux of the Matter What we’re trying to achieve is quite simple.

Let’s say we have a bunch of shapes: circles, squares, triangles, and we want to call draw() on each one. C++ Approach #1: Virtual Functions The first way to do this that comes to mind in C++ is through virtual functions, which makes use of runtime polymorphism. The vtable pointer lives inside the object, virtual dispatch happens automatically.std::vector<Shape*> shapes = { new Circle(), new Square() };for (auto* s : shapes)s->draw(); Rust’s equivalent would be dyn Trait, which is what we ultimately want to understand.

But first, let’s take a look at another way we could solve this in C++. C++ Approach #2: CRTPOne could also go the CRTP (Curiously Recurring Template Pattern) route, which is essentially compile time polymorphism. If you’re interested, this awesome talk by Klaus Iglberger was my first introduction to the topic, and the one I keep coming back to for reference.template<typename Derived>struct Shape {void draw() {static_cast<Derived*>(this)->draw();}}; Essentially, there are no vtables and it’s resolved at compile time, sacrificing readability (it really is a mouthful).

Rust offers a much more straightforward and simple equivalent to CRTP, namely monomorphization. This is the approach we’ll dig into first to start constructing our mental model of what Rust has to offer. Static Dispatch Photo by Jiawei Zhao on Unsplash Static dispatch, also known as generics, achieves a similar result to CRTP: the compiler generates a separate copy of the function for each type it’s called with.

There is zero runtime cost, but the types must be known at compile time.trait Draw {fn draw(&self) -> &str;}struct Circle;struct Square;impl Draw for Circle {fn draw(&self) -> &str {.