<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Rust on Sofía Belén López Vicens</title><link>https://sofiabelen.github.io/categories/rust/</link><description>Recent content in Rust on Sofía Belén López Vicens</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><lastBuildDate>Fri, 04 Sep 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://sofiabelen.github.io/categories/rust/index.xml" rel="self" type="application/rss+xml"/><item><title>Visualizing Rust's Vtables: How dyn Trait Works In Memory</title><link>https://sofiabelen.github.io/projects/visualizing-rusts-vtables-how-dyn-trait-works-in-memory/</link><pubDate>Fri, 04 Sep 2026 00:00:00 +0000</pubDate><guid>https://sofiabelen.github.io/projects/visualizing-rusts-vtables-how-dyn-trait-works-in-memory/</guid><description>&lt;img src="https://sofiabelen.github.io/projects/visualizing-rusts-vtables-how-dyn-trait-works-in-memory/Rust_fungus_-_Flickr_-_gailhampshire.jpg" alt="Featured image of post Visualizing Rust's Vtables: How dyn Trait Works In Memory" />&lt;p>I&amp;rsquo;m venturing into Rust and it&amp;rsquo;s both satisfying and mind-boggling at the same time. So far I&amp;rsquo;ve been learning from &lt;a class="link" href="https://doc.rust-lang.org/book/" target="_blank" rel="noopener"
>the book&lt;/a> and &lt;a class="link" href="https://mara.nl/atomics/" target="_blank" rel="noopener"
>Mara Bos&amp;rsquo; book&lt;/a>, but I got the itch to do some dissecting myself. My initial goal of these experiments was to compare Rust&amp;rsquo;s approach to polymorphism with C++&amp;rsquo;s. Ultimately, however, as I&amp;rsquo;ve come to realize, it&amp;rsquo;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&amp;rsquo;t treat Rust as C++ with different syntax. If that were the case, there&amp;rsquo;d be nothing revolutionary about it.&lt;/p>
&lt;p>That said, I believe there is merit in poking around and coming to understand the &lt;em>why&lt;/em>. So, if you&amp;rsquo;re like me and need to know &lt;em>what exactly is happening in memory,&lt;/em> in order to feel like you truly understand the concepts, hopefully you&amp;rsquo;ll find this post useful :)&lt;/p>
&lt;p>By the way, the thumbnail image is a photo of the rust fungus, to which we owe Rust&amp;rsquo;s name.
Credit: &lt;a href="https://commons.wikimedia.org/wiki/File:Rust_fungus_-_Flickr_-_gailhampshire.jpg">gailhampshire from Cradley, Malvern, U.K&lt;/a>, &lt;a href="https://creativecommons.org/licenses/by/2.0">CC BY 2.0&lt;/a>, via Wikimedia Commons.&lt;/p>
&lt;p>You can find all the code and experiments on &lt;a class="link" href="https://github.com/sofiabelen/rust-vtables-for-cpp-programmers" target="_blank" rel="noopener"
>GitHub&lt;/a>.&lt;/p>
&lt;h2 id="introduction-the-crux-of-the-matter">Introduction: The Crux of the Matter
&lt;/h2>&lt;p>What we&amp;rsquo;re trying to achieve is quite simple. Let&amp;rsquo;s say we have a bunch of shapes: circles, squares, triangles, and we want to call &lt;code>draw()&lt;/code> on each one.&lt;/p>
&lt;h3 id="c-approach-1-virtual-functions">C++ Approach #1: Virtual Functions
&lt;/h3>&lt;p>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.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">vector&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">Shape&lt;/span>&lt;span class="o">*&amp;gt;&lt;/span> &lt;span class="n">shapes&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nf">Circle&lt;/span>&lt;span class="p">(),&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="n">Square&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="k">auto&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="nl">s&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">shapes&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">s&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">draw&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Rust&amp;rsquo;s equivalent would be &lt;code>dyn Trait&lt;/code>, which is what we ultimately want to understand. But first, let&amp;rsquo;s take a look at another way we could solve this in C++.&lt;/p>
&lt;h3 id="c-approach-2-crtp">C++ Approach #2: CRTP
&lt;/h3>&lt;p>One could also go the CRTP (Curiously Recurring Template Pattern) route, which is essentially compile time polymorphism. If you&amp;rsquo;re interested, this awesome &lt;a class="link" href="https://www.youtube.com/watch?v=pmdwAf6hCWg" target="_blank" rel="noopener"
>talk&lt;/a> by Klaus Iglberger was my first introduction to the topic, and the one I keep coming back to for reference.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="k">template&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="k">typename&lt;/span> &lt;span class="n">Derived&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">struct&lt;/span> &lt;span class="nc">Shape&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">void&lt;/span> &lt;span class="nf">draw&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">static_cast&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">Derived&lt;/span>&lt;span class="o">*&amp;gt;&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">this&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">draw&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Essentially, there are no vtables and it&amp;rsquo;s resolved at compile time, sacrificing readability (it really is a mouthful).&lt;/p>
&lt;p>Rust offers a much more straightforward and simple equivalent to CRTP, namely monomorphization. This is the approach we&amp;rsquo;ll dig into first to start constructing our mental model of what Rust has to offer.&lt;/p>
&lt;h2 id="static-dispatch">Static Dispatch
&lt;/h2>&lt;figure>
&lt;img src="jiawei-zhao-W-ypTC6R7_k-unsplash.jpg" alt="">
&lt;caption>
Photo by &lt;a href="https://unsplash.com/@jiaweizhao?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Jiawei Zhao&lt;/a> on &lt;a href="https://unsplash.com/photos/tuxedo-cat-in-brown-cardboard-box-W-ypTC6R7_k?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash&lt;/a>
&lt;/caption>
&lt;/figure>
&lt;p>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&amp;rsquo;s called with. There is zero runtime cost, but the types must be known at compile time.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-rust" data-lang="rust">&lt;span class="line">&lt;span class="cl">&lt;span class="k">trait&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Draw&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">fn&lt;/span> &lt;span class="nf">draw&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>-&amp;gt; &lt;span class="kp">&amp;amp;&lt;/span>&lt;span class="kt">str&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">struct&lt;/span> &lt;span class="nc">Circle&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">struct&lt;/span> &lt;span class="nc">Square&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">impl&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Draw&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">for&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Circle&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">fn&lt;/span> &lt;span class="nf">draw&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>-&amp;gt; &lt;span class="kp">&amp;amp;&lt;/span>&lt;span class="kt">str&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;Drawing a circle&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">impl&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Draw&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">for&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Square&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">fn&lt;/span> &lt;span class="nf">draw&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>-&amp;gt; &lt;span class="kp">&amp;amp;&lt;/span>&lt;span class="kt">str&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;Drawing a square&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">fn&lt;/span> &lt;span class="nf">draw_shape&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">T&lt;/span>: &lt;span class="nc">Draw&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">shape&lt;/span>: &lt;span class="nc">T&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="fm">println!&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="si">{}&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">shape&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">draw&lt;/span>&lt;span class="p">());&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">fn&lt;/span> &lt;span class="nf">main&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">circle&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Circle&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">square&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Square&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">draw_shape&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">circle&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">draw_shape&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">square&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Under the hood, the compiler generates two separate functions: &lt;code>draw_shape::&amp;lt;Circle&amp;gt;&lt;/code> and &lt;code>draw_shape::&amp;lt;Square&amp;gt;&lt;/code>.&lt;/p>
&lt;h3 id="how-does-this-compare-to-cs-templates">How does this compare to C++&amp;rsquo;s templates?
&lt;/h3>&lt;p>The difference here is the philosophy. C++ makes the constraints implicit, a
template accepts any type &lt;code>T&lt;/code> that happens to have a &lt;code>.draw()&lt;/code> method. While,
in Rust, you are typing out the contract explicitly: you &amp;ldquo;implement the Draw
trait for Square.&amp;rdquo;&lt;/p>
&lt;p>My question is, when is this not enough? Before tackling this question, let&amp;rsquo;s indulge a bit in a side quest.&lt;/p>
&lt;h3 id="side-quest-rusts-zero-sized-types">Side Quest: Rust&amp;rsquo;s Zero-Sized Types
&lt;/h3>&lt;p>I tried to look at the size of &lt;code>Circle&lt;/code> and &lt;code>Square&lt;/code> because I wanted to make
the comparison to wide pointers, which we&amp;rsquo;ll see in a bit, but it led me to
discover something unexpected. In C++, the standard mandates that every object
has a size of at least 1 byte, even if empty. This
is so that two distinct objects always have distinct address, meaning &lt;code>&amp;amp;obj1&lt;/code>
must be different from &lt;code>&amp;amp;obj2&lt;/code>. This is one of those things that is ingrained in my mind as a fact of
nature, so seeing that rust returns &lt;code>0&lt;/code> totally surprised me. These are the little moments that bring me so much joy as I&amp;rsquo;m
exploring Rust because it deconstructs my mental model and helps me appreciate the different philosophy.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-rust" data-lang="rust">&lt;span class="line">&lt;span class="cl">&lt;span class="fm">println!&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="si">{}&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">std&lt;/span>::&lt;span class="n">mem&lt;/span>::&lt;span class="n">size_of&lt;/span>::&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">Circle&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">());&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// 0 WHAT???
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="fm">println!&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="si">{}&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">std&lt;/span>::&lt;span class="n">mem&lt;/span>::&lt;span class="n">size_of&lt;/span>::&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">Square&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">());&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// 0
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This is how I discovered that Rust handles the unique address guarantee differently. Zero-sized types (ZST) are structs that don&amp;rsquo;t contain any fields, therefore there&amp;rsquo;s no need to allocate any memory. Rust tracks identity through ownership, not addresses. Every value has exactly one owner at a time, this is enforced at compile time by our friend, the borrow-checker.&lt;/p>
&lt;p>In C++, we might do this to check if two pointers refer to the same object:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">a&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">b&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="c1">// same object }
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>In Rust, that question is answered by the borrow-checker at compile time:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-rust" data-lang="rust">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// the borrow checker already knows these are different bindings
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// you don&amp;#39;t need to compare addresses to tell them apart
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Circle&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">b&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Circle&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The compiler tracks &lt;code>a&lt;/code> and &lt;code>b&lt;/code> as distinct names with distinct owners.&lt;/p>
&lt;p>After learning about this, my question was, what happens then if we take the address of a zst? Let&amp;rsquo;s try it.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-rust" data-lang="rust">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Circle&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">b&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Circle&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="fm">println!&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="si">{:p}&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">as&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="k">const&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Circle&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="fm">println!&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="si">{:p}&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">b&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">as&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="k">const&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Circle&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The output:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">0x7ffdda99aece
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">0x7ffdda99aecf
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Strange&amp;hellip; so they are getting distinct stack addresses 1 byte apart (&lt;code>ce&lt;/code> and &lt;code>cf&lt;/code> in hex). It might look like the compiler allocated a byte for each, just as C++ would, but this is simply a debug-mode behavior. The compiler assigns local ZST variables a dummy stack slot purely so debuggers can track and inspect them by reference.&lt;/p>
&lt;p>If we try this in release mode, however, we get different behavior:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">cargo run --release --bin 01_static_dispatch
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The addresses do indeed collapse for me:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">0x7ffdf74afa6f
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">0x7ffdf74afa6f
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>My take away from this is that the compiler makes no guarantees about ZST addresses, and identity is tracked by the borrow checker through ownership, not memory addresses.&lt;/p>
&lt;h2 id="dynamic-dispatch">Dynamic Dispatch
&lt;/h2>&lt;figure>
&lt;img src="aldrin-rachman-pradana-rrzPbEZR1I4-unsplash.jpg" alt="">
&lt;caption>
Photo by &lt;a href="https://unsplash.com/@aldrinrachmanpradana?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Aldrin Rachman Pradana&lt;/a> on &lt;a href="https://unsplash.com/photos/a-cat-sitting-on-the-back-of-a-motorcycle-rrzPbEZR1I4?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash&lt;/a>
&lt;/caption>
&lt;/figure>
&lt;p>To continue on our main quest, let&amp;rsquo;s see what dynamic dispatch would look like for our previous example:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-rust" data-lang="rust">&lt;span class="line">&lt;span class="cl">&lt;span class="k">fn&lt;/span> &lt;span class="nf">draw_shape&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">shape&lt;/span>: &lt;span class="kp">&amp;amp;&lt;/span>&lt;span class="nc">dyn&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Draw&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="fm">println!&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="si">{}&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">shape&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">draw&lt;/span>&lt;span class="p">());&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">fn&lt;/span> &lt;span class="nf">main&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">circle&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Circle&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">square&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Square&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">draw_shape&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">circle&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">draw_shape&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">square&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>It looks almost identical to the static dispatch version, the only difference is &lt;code>&amp;amp;dyn Draw&lt;/code> instead of &lt;code>&amp;lt;T: Draw&amp;gt;&lt;/code>. But something fundamentally different is happening underneath. Let&amp;rsquo;s see what happens to the size:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-rust" data-lang="rust">&lt;span class="line">&lt;span class="cl">&lt;span class="fm">println!&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;&amp;amp;Circle size: &lt;/span>&lt;span class="si">{}&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">std&lt;/span>::&lt;span class="n">mem&lt;/span>::&lt;span class="n">size_of&lt;/span>::&lt;span class="o">&amp;lt;&amp;amp;&lt;/span>&lt;span class="n">Circle&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">());&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// 8
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="fm">println!&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;&amp;amp;dyn Draw size: &lt;/span>&lt;span class="si">{}&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">std&lt;/span>::&lt;span class="n">mem&lt;/span>::&lt;span class="n">size_of&lt;/span>::&lt;span class="o">&amp;lt;&amp;amp;&lt;/span>&lt;span class="k">dyn&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Draw&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">());&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// 16
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>&amp;amp;dyn Draw&lt;/code> is twice the size of a regular pointer. This is called a &lt;strong>wide pointer&lt;/strong>: it&amp;rsquo;s actually two pointers, one points to the data, the other to a vtable. That vtable is what tells Rust which &lt;code>draw()&lt;/code> to call at runtime.&lt;/p>
&lt;p>We can inspect what those pointers look like:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-rust" data-lang="rust">&lt;span class="line">&lt;span class="cl">&lt;span class="k">fn&lt;/span> &lt;span class="nf">inspect&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">shape&lt;/span>: &lt;span class="kp">&amp;amp;&lt;/span>&lt;span class="nc">dyn&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Draw&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">data_ptr&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">vtable_ptr&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">unsafe&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">std&lt;/span>::&lt;span class="n">mem&lt;/span>::&lt;span class="n">transmute&lt;/span>::&lt;span class="o">&amp;lt;&amp;amp;&lt;/span>&lt;span class="k">dyn&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Draw&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">usize&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">usize&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">shape&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">};&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="fm">println!&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;data ptr: &lt;/span>&lt;span class="si">{:#x}&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">data_ptr&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="fm">println!&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;vtable ptr: &lt;/span>&lt;span class="si">{:#x}&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">vtable_ptr&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>std::mem::transmute&lt;/code> does a bit-for-bit copy from the source type (&lt;code>&amp;amp;dyn Draw&lt;/code>) to the destination type (&lt;code>(usize, usize)&lt;/code>). This requires &lt;code>unsafe&lt;/code> because the compiler cannot guarantee that arbitrary bit patterns form valid values for the target type.&lt;/p>
&lt;p>We see that objects of the same type share a vtable and the data pointer changes per instance:&lt;/p>
&lt;pre>&lt;code>
=== circle ===
data ptr: 0x7ffdcae73286
&lt;mark>vtable ptr: 0x55f23cbe5338 &lt;-- circle's vtable&lt;/mark>
=== circle2 ===
data ptr: 0x7ffdcae732ec
&lt;mark>vtable ptr: 0x55f23cbe5338 &lt;-- same vtable as circle!!&lt;/mark>
=== square ===
data ptr: 0x7ffdcae73287
vtable ptr: 0x55f23cbe5358
&lt;/pre>&lt;/code>
&lt;h2 id="why-the-need-for-dynamic-dispatch">Why the Need for Dynamic Dispatch
&lt;/h2>&lt;p>Getting back to our question, when is static dispatch not enough, and why do we need dynamic dispatch at all? Let&amp;rsquo;s explore a scenario where static dispatch is not enough. If we wanted to create a &lt;code>Vec&amp;lt;T&amp;gt;&lt;/code> containing a mix of both &lt;code>Square&lt;/code>s and &lt;code>Circle&lt;/code>s, we hit a wall with generics alone.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-rust" data-lang="rust">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// does not compile!!
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">shapes&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="fm">vec!&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">Circle&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Square&lt;/span>&lt;span class="p">];&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>A &lt;code>Vec&amp;lt;T&amp;gt;&lt;/code> requires every element to be the exact same &lt;strong>type&lt;/strong> and &lt;strong>size&lt;/strong>. &lt;code>Circle&lt;/code> and &lt;code>Square&lt;/code> are completely unrelated and could have different sizes. There&amp;rsquo;s no &amp;ldquo;base class&amp;rdquo; like in C++, where you&amp;rsquo;d write:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">vector&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">Shape&lt;/span>&lt;span class="o">*&amp;gt;&lt;/span> &lt;span class="n">shapes&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nf">Circle&lt;/span>&lt;span class="p">(),&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="n">Square&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This is where we need dynamic dispatch, aka &lt;code>dyn Trait&lt;/code>. &lt;code>Box&amp;lt;T&amp;gt;&lt;/code> is Rust&amp;rsquo;s way of allocating a value on the heap and owning it through a pointer.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-rust" data-lang="rust">&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">shapes&lt;/span>&lt;span class="w"> &lt;/span>: &lt;span class="nb">Vec&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="nb">Box&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="k">dyn&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Draw&lt;/span>&lt;span class="o">&amp;gt;&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="fm">vec!&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// Box -&amp;gt; [ data ptr | vtable ptr ]
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">Box&lt;/span>::&lt;span class="n">new&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Circle&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nb">Box&lt;/span>::&lt;span class="n">new&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Square&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">];&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>Box&amp;lt;dyn Draw&amp;gt;&lt;/code> solves the size problem. A &lt;code>Box&lt;/code> is always the same size, since it&amp;rsquo;s just a wide pointer.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">Vec&amp;lt;Box&amp;lt;dyn Draw&amp;gt;&amp;gt; in memory:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">[ 16 bytes | 16 bytes ]
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ↓ ↓
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">[data|vtable] [data|vtable]
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ↓ ↓
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Circle Square
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>A key distinction from C++ philosophy is that in C++, the choice between dynamic and static dispatch is made at the class level. If you mark a method &lt;code>virtual&lt;/code>, that class will always use dynamic dispatch. In the case of &lt;code>vector&amp;lt;Shape*&amp;gt;&lt;/code>, it works because the vtable pointer is part of the object.&lt;/p>
&lt;p>In Rust, the choice is made at the &lt;em>call site.&lt;/em> &lt;code>Circle&lt;/code> is just &lt;code>Circle&lt;/code>, it knows nothing about dispatch. You decide whether to use static or dynamic dispatch depending on how you refer to it: &lt;code>&amp;amp;Circle&lt;/code> for static, &lt;code>&amp;amp;dyn Draw&lt;/code> for dynamic.&lt;/p>
&lt;figure>
&lt;img style='height: 60%; width: 60%; object-fit: contain' src="pexels-andersonportella-30131184.jpg">
&lt;figcaption>
Photo by &lt;a href="https://www.pexels.com/photo/aerial-silk-performer-in-dramatic-pose-on-stage-30131184/">Anderson Portella&lt;/a>.
&lt;/figcaption>
&lt;/figure>
&lt;p>I honestly had to take a moment to let all of this sink in, since I&amp;rsquo;m so used to how C++ does things, it twists my brain (in a good way) to reason about Rust&amp;rsquo;s philosophy. I&amp;rsquo;ve recently started doing aerial silks, and there&amp;rsquo;s an odd resemblance: when you&amp;rsquo;re inverted in the air, you have to consciously rewire your entire sense of how your body works. Rust does the same thing to your mental model.&lt;/p>
&lt;h2 id="one-vtable-per-type-trait-pair">One Vtable per (Type, Trait) Pair
&lt;/h2>&lt;p>Let&amp;rsquo;s see what happens when we combine different traits for a type. We want our &lt;code>Duck&lt;/code> to both &lt;code>Fly&lt;/code> and &lt;code>Swim&lt;/code>.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-rust" data-lang="rust">&lt;span class="line">&lt;span class="cl">&lt;span class="k">trait&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Fly&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">fn&lt;/span> &lt;span class="nf">fly&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>-&amp;gt; &lt;span class="kp">&amp;amp;&lt;/span>&lt;span class="kt">str&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">trait&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Swim&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">fn&lt;/span> &lt;span class="nf">swim&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>-&amp;gt; &lt;span class="kp">&amp;amp;&lt;/span>&lt;span class="kt">str&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">struct&lt;/span> &lt;span class="nc">Duck&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">impl&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Fly&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">for&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Duck&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">fn&lt;/span> &lt;span class="nf">fly&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>-&amp;gt; &lt;span class="kp">&amp;amp;&lt;/span>&lt;span class="kt">str&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;Duck flies!&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">impl&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Swim&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">for&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Duck&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">fn&lt;/span> &lt;span class="nf">swim&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>-&amp;gt; &lt;span class="kp">&amp;amp;&lt;/span>&lt;span class="kt">str&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;Duck swims!&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>What does this look like in memory:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-rust" data-lang="rust">&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">fly_obj&lt;/span>: &lt;span class="kp">&amp;amp;&lt;/span>&lt;span class="nc">dyn&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Fly&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">duck&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">swim_obj&lt;/span>: &lt;span class="kp">&amp;amp;&lt;/span>&lt;span class="nc">dyn&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Swim&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">duck&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">data_fly&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">vtable_fly&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">unsafe&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">std&lt;/span>::&lt;span class="n">mem&lt;/span>::&lt;span class="n">transmute&lt;/span>::&lt;span class="o">&amp;lt;&amp;amp;&lt;/span>&lt;span class="k">dyn&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Fly&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">usize&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">usize&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">fly_obj&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">};&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">data_swim&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">vtable_swim&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">unsafe&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">std&lt;/span>::&lt;span class="n">mem&lt;/span>::&lt;span class="n">transmute&lt;/span>::&lt;span class="o">&amp;lt;&amp;amp;&lt;/span>&lt;span class="k">dyn&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Swim&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">usize&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">usize&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">swim_obj&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">};&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="fm">println!&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;fly_obj -&amp;gt; data: &lt;/span>&lt;span class="si">{:#x}&lt;/span>&lt;span class="s"> vtable: &lt;/span>&lt;span class="si">{:#x}&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">data_fly&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">vtable_fly&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="fm">println!&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;swim_obj -&amp;gt; data: &lt;/span>&lt;span class="si">{:#x}&lt;/span>&lt;span class="s"> vtable: &lt;/span>&lt;span class="si">{:#x}&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">data_swim&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">vtable_swim&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="fm">println!&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;size of duck: &lt;/span>&lt;span class="si">{}&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">std&lt;/span>::&lt;span class="n">mem&lt;/span>::&lt;span class="n">size_of_val&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">duck&lt;/span>&lt;span class="p">));&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;pre>&lt;code>
fly_obj -> data: &lt;mark>0x7ffe769558df&lt;/mark> vtable: &lt;mark>0x5573a247ea48&lt;/mark>
swim_obj -> data: &lt;mark>0x7ffe769558df&lt;/mark> vtable: &lt;mark>0x5573a247ea68&lt;/mark>
size of duck: 0
&lt;/pre>&lt;/code>
&lt;p>We can marvel again at how the size of &lt;code>Duck&lt;/code> is &lt;code>0&lt;/code> because it&amp;rsquo;s a ZST. We also see that the &lt;code>fly_obj&lt;/code> and the &lt;code>swim_obj&lt;/code> both share the same &lt;strong>data pointer&lt;/strong>, which makes sense because they are both dynamic traits of the same underlying duck object. The interesting part here, however, is how the &lt;strong>vtable pointers&lt;/strong> differ.&lt;/p>
&lt;figure style="display: flex; flex-direction: column; align-items: center; text-align: center; margin: 0 auto;">
&lt;img style="height: 60%; width: 60%; object-fit: contain;" src="ross-sokolovski-kCZSzqvIei4-unsplash.jpg" alt="Brown and white duck on gray concrete floor">
&lt;figcaption style="margin-top: 8px;">
A duck is just a duck.&lt;br>
Photo by &lt;a href="https://unsplash.com/@ross_sokolovski?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Ross Sokolovski&lt;/a> on &lt;a href="https://unsplash.com/photos/brown-and-white-duck-on-gray-concrete-floor-kCZSzqvIei4?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash&lt;/a>
&lt;/figcaption>
&lt;/figure>
&lt;p>This reinforces our core idea: the vtable is not embedded in the object (like C++), it&amp;rsquo;s external static data that gets paired with the object when you ask for dynamic dispatch. A &lt;code>Duck&lt;/code> stays a &lt;code>Duck&lt;/code> no matter if it swims or flies, or how many traits it implements.&lt;/p>
&lt;h2 id="object-safety-why-not-every-trait-can-be-dyn">Object Safety: Why Not Every Trait Can Be Dyn
&lt;/h2>&lt;p>If you&amp;rsquo;re like me, you&amp;rsquo;re probably thinking that is all very nice. And it is, but it&amp;rsquo;s important to also understand the limitations. One of these limitations is something that in C++ we didn&amp;rsquo;t have to worry about, and another one is also present in C++. To clear up the mystery, not every trait in Rust can be used as &lt;code>dyn Trait&lt;/code>. A trait must follow so-called &lt;strong>object safety&lt;/strong> rules to be used as a trait object:&lt;/p>
&lt;ul>
&lt;li>methods can&amp;rsquo;t return &lt;code>Self&lt;/code>&lt;/li>
&lt;li>methods can&amp;rsquo;t have generic parameters&lt;/li>
&lt;/ul>
&lt;h3 id="methods-cant-return-self">Methods Can&amp;rsquo;t Return Self
&lt;/h3>&lt;p>&lt;code>Clone&lt;/code> is the classic example because it has &lt;code>fn clone(&amp;amp;self) -&amp;gt; Self&lt;/code>.&lt;/p>
&lt;p>&lt;code>Self&lt;/code> is essentially just a placeholder for the type that is currently implementing the trait.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-rust" data-lang="rust">&lt;span class="line">&lt;span class="cl">&lt;span class="k">trait&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">Clone&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">fn&lt;/span> &lt;span class="nf">clone&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>-&amp;gt; &lt;span class="nc">Self&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>So, for &lt;code>Circle&lt;/code> implementing the &lt;code>Clone&lt;/code> trait, &lt;code>Self&lt;/code> resolves to &lt;code>Circle&lt;/code>.&lt;/p>
&lt;p>Since it returns &lt;code>Self&lt;/code>, it means that the caller needs to know the concrete type to know how much memory to allocate for the return value. Through a vtable, the caller doesn&amp;rsquo;t know the concrete type, so the compiler rejects it.&lt;/p>
&lt;p>C++ doesn&amp;rsquo;t have this problem at all, since virtual dispatch always goes through pointers and return types are always pointers too. Rust works with values directly, so when you return &lt;code>Self&lt;/code> by value, you need to know the size.&lt;/p>
&lt;h3 id="methods-cant-have-generic-parameters">Methods Can&amp;rsquo;t Have Generic Parameters
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">trait Serialize {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> fn serialize&amp;lt;T&amp;gt;(&amp;amp;self, output: &amp;amp;mut T);
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>In this case, the compiler would need a separate vtable entry for every possible &lt;code>T&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">serialize::&amp;lt;File&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">serialize::&amp;lt;String&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">serialize::&amp;lt;Vec&amp;lt;u8&amp;gt;&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">...
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>It would essentially need to be infinite.&lt;/p>
&lt;p>Therefore, this won&amp;rsquo;t compile:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-rust" data-lang="rust">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">let&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">s&lt;/span>: &lt;span class="nb">Box&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="k">dyn&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Serialize&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">..&lt;/span>&lt;span class="p">.;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">// COMPILE ERROR
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>What about C++? It hits the same wall essentially, since you can&amp;rsquo;t have a template virtual method for the same reason:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">Serialize&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">public&lt;/span>&lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">template&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="k">typename&lt;/span> &lt;span class="n">T&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">virtual&lt;/span> &lt;span class="kt">void&lt;/span> &lt;span class="n">serialize&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">T&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="n">output&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// COMPILE ERROR!!
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>I googled this, and technically, with enough C++20 black magic you &lt;em>could&lt;/em> work around this by building the vtable manually, but it only works within a single source file and is not something you&amp;rsquo;d do in production code. This is out of the scope of my post, but I&amp;rsquo;ll leave a link to &lt;a class="link" href="https://dev.to/christiandaley/virtual-function-templates-with-stateful-metapogramming-in-c-20-33l2" target="_blank" rel="noopener"
>Christian Daley&amp;rsquo;s article&lt;/a> if you&amp;rsquo;d like to go down this rabbit hole.&lt;/p>
&lt;h2 id="recap">Recap
&lt;/h2>&lt;p>We started with a simple question: how do you call &lt;code>draw()&lt;/code> on a collection of shapes in Rust, without inheritance?&lt;/p>
&lt;ul>
&lt;li>
&lt;p>Monomorphization (static dispatch) in Rust and CRTP in C++ both are compile-time polymorphism. They have no runtime cost, with the downside being the code size. Essentially, monomorphization is a native language feature for what CRTP achieves as a sort of workaround.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;code>dyn Trait&lt;/code> and virtual functions in C++ both use a vtable to achieve dynamic dispatch. The difference is that in C++, the vtable pointer lives inside the object, adding overhead to every instance whether you use polymorphism or not. In Rust, the vtable pointer only appears when you explicitly use &lt;code>&amp;amp;dyn Trait&lt;/code> or &lt;code>Box&amp;lt;dyn Trait&amp;gt;&lt;/code>.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Zero-sized types (ZSTs): In C++ every object must occupy at least 1 byte because it tracks the identity through memory addresses at runtime. In Rust, identity is tracked through ownership at compile time, which is why it can have zero-sized types.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>If you made it this far, thank you! This one took a while to get it out there.
I started writing it in March 2026. It just needed some final touches, but I
hadn&amp;rsquo;t gotten around to publishing it. I kept second guessing myself if it&amp;rsquo;d
actually be useful content, but I figured, if this approach was helpful for me,
maybe it&amp;rsquo;d also be useful for someone else. Much has happened between then and now, both
in my personal life and in my Rust journey. Some good, some bad. I&amp;rsquo;m
in Spain now, and a life update post is coming soon. On the Rust side, I&amp;rsquo;m
diving into concurrency and lock-free programming,
with the goal of eventually contributing to open source. And I plan to document
that journey here too :)&lt;/p></description></item></channel></rss>