<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://nadrieril.github.io/blog/feed.xml" rel="self" type="application/atom+xml" /><link href="https://nadrieril.github.io/blog/" rel="alternate" type="text/html" /><updated>2026-03-22T06:36:36+00:00</updated><id>https://nadrieril.github.io/blog/feed.xml</id><title type="html">Nadri’s musings</title><author><name>Nadri</name></author><entry><title type="html">What If Traits Carried Values</title><link href="https://nadrieril.github.io/blog/2026/03/22/what-if-traits-carried-values.html" rel="alternate" type="text/html" title="What If Traits Carried Values" /><published>2026-03-22T06:30:00+00:00</published><updated>2026-03-22T06:30:00+00:00</updated><id>https://nadrieril.github.io/blog/2026/03/22/what-if-traits-carried-values</id><content type="html" xml:base="https://nadrieril.github.io/blog/2026/03/22/what-if-traits-carried-values.html"><![CDATA[<p>In my <a href="https://nadrieril.github.io/blog/2026/03/20/dictionary-passing-style.html">last post</a>,
I showed you how traits behave like passing a bundle of methods between functions,
except automatically inferred by the compiler.</p>

<p><a href="https://github.com/tmandry">Tyler Mandry</a> <a href="https://rust-lang.zulipchat.com/#narrow/channel/144729-t-types/topic/A.20calculus.20for.20dictionary-passing-style/near/580762824">was quick to point
out</a>
that this looks just like
<a href="https://tmandry.gitlab.io/blog/posts/2021-12-21-context-capabilities">contexts/capabilities</a>.</p>

<p>In this post I’ll explore the underlying question: what if trait bounds also carried values?</p>

<h2 id="contexts-and-capabilities">Contexts and Capabilities</h2>

<p>We’ll start with a Rust feature idea I’ve been giddy about since I came across it, well-presented by
Tyler in <a href="https://tmandry.gitlab.io/blog/posts/2021-12-21-context-capabilities">his blog post</a>.
I recommend giving it a read, but I’ll summarize the core idea.</p>

<p>The feature has three components:</p>

<ol>
  <li>You declare a global name with <code class="language-plaintext highlighter-rouge">capability my_capability;</code>;</li>
  <li>You can now write <code class="language-plaintext highlighter-rouge">my_capability: Type</code> in a <code class="language-plaintext highlighter-rouge">where</code> bound, and this works like an implicit argument:
the compiler will pass you a value and error if it can’t find one;</li>
  <li>A value is provided for a given scope by writing: <code class="language-plaintext highlighter-rouge">with my_capability = some_value() { ... }</code>.</li>
</ol>

<p>This is particularly awesome in trait impls:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">capability</span> <span class="n">arena</span><span class="p">;</span>

<span class="k">impl</span><span class="o">&lt;</span><span class="nv">'a</span><span class="o">&gt;</span> <span class="n">Deserialize</span> <span class="k">for</span> <span class="o">&amp;</span><span class="nv">'a</span> <span class="n">Foo</span>
<span class="k">where</span>
    <span class="n">arena</span><span class="p">:</span> <span class="o">&amp;</span><span class="nv">'a</span> <span class="n">BasicArena</span><span class="p">,</span>
<span class="p">{</span>
    <span class="o">...</span>
<span class="p">}</span>

<span class="k">fn</span> <span class="nf">main</span><span class="p">()</span> <span class="k">-&gt;</span> <span class="nb">Result</span><span class="o">&lt;</span><span class="p">(),</span> <span class="n">Error</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="k">let</span> <span class="n">bytes</span> <span class="o">=</span> <span class="nf">read_some_bytes</span><span class="p">()</span><span class="o">?</span><span class="p">;</span>
    <span class="n">with</span> <span class="n">arena</span> <span class="o">=</span> <span class="o">&amp;</span><span class="nn">arena</span><span class="p">::</span><span class="nn">BasicArena</span><span class="p">::</span><span class="nf">new</span><span class="p">()</span> <span class="p">{</span>
        <span class="k">let</span> <span class="n">foos</span><span class="p">:</span> <span class="nb">Vec</span><span class="o">&lt;&amp;</span><span class="n">Foo</span><span class="o">&gt;</span> <span class="o">=</span> <span class="nf">deserialize</span><span class="p">(</span><span class="n">bytes</span><span class="p">)</span><span class="o">?</span><span class="p">;</span>
        <span class="nd">println!</span><span class="p">(</span><span class="s">"foos: {:?}"</span><span class="p">,</span> <span class="n">foos</span><span class="p">);</span>
    <span class="p">}</span>
    <span class="nf">Ok</span><span class="p">(())</span>
<span class="p">}</span>
</code></pre></div></div>

<p>What happens here is that the dictionary<sup id="fnref:3" role="doc-noteref"><a href="#fn:3" class="footnote" rel="footnote">1</a></sup> for <code class="language-plaintext highlighter-rouge">&lt;&amp;Foo as Deserialize&gt;</code> now also carries a runtime
value.
The compiler threads it through any intermediate functions/impls that have a <code class="language-plaintext highlighter-rouge">T: Deserialize</code> bound,
without them needing to know about it (well, see next section).</p>

<h2 id="dictionaries-carry-values-now">Dictionaries carry values now</h2>

<p>Implicitly threading values between unsuspecting functions does change things a bit, of course.</p>

<h3 id="controlling-the-implicit-value">Controlling the implicit value</h3>

<p>As with any Rust generics, we’ll need a way to control a bit which values we can support.
Tyler proposes:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">fn</span> <span class="n">deserialize_and_print_later</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span><span class="p">(</span><span class="n">deserializer</span><span class="p">:</span> <span class="o">&amp;</span><span class="k">mut</span> <span class="n">Deserializer</span><span class="p">)</span>
<span class="k">where</span> <span class="nf">with</span><span class="p">(</span><span class="k">'static</span> <span class="o">+</span> <span class="nb">Send</span><span class="p">)</span> <span class="n">T</span><span class="p">:</span> <span class="n">Deserialize</span>
<span class="p">{</span> <span class="o">...</span> <span class="p">}</span>
</code></pre></div></div>
<p>In our dictionary world, we may just as well write:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">fn</span> <span class="n">deserialize_and_print_later</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span><span class="p">(</span><span class="n">deserializer</span><span class="p">:</span> <span class="o">&amp;</span><span class="k">mut</span> <span class="n">Deserializer</span><span class="p">)</span>
<span class="k">where</span>
    <span class="n">T</span><span class="p">:</span> <span class="n">Deserialize</span><span class="p">,</span>
    <span class="o">&lt;</span><span class="n">T</span> <span class="k">as</span> <span class="n">Deserialize</span><span class="o">&gt;</span><span class="p">:</span> <span class="k">'static</span> <span class="o">+</span> <span class="nb">Send</span><span class="p">,</span>
<span class="p">{</span> <span class="o">...</span> <span class="p">}</span>
</code></pre></div></div>

<p>where <code class="language-plaintext highlighter-rouge">&lt;T as Deserialize&gt;</code> is understood to refer to the dictionary itself,
so we may apply bounds on it like any other type<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">2</a></sup>.</p>

<h3 id="linearity">Linearity</h3>

<p>Perhaps the craziest consequence of taking this seriously is that
trait bounds need ownership semantics now.
Imagine:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">impl</span><span class="o">&lt;</span><span class="nv">'a</span><span class="o">&gt;</span> <span class="n">Deserialize</span> <span class="k">for</span> <span class="o">&amp;</span><span class="nv">'a</span> <span class="n">Foo</span>
<span class="k">where</span>
    <span class="n">arena</span><span class="p">:</span> <span class="o">&amp;</span><span class="nv">'a</span> <span class="k">mut</span> <span class="nb">Vec</span><span class="o">&lt;</span><span class="n">Foo</span><span class="o">&gt;</span><span class="p">,</span>
<span class="p">{</span>
    <span class="o">...</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Now the dictionary contains a <code class="language-plaintext highlighter-rouge">&amp;mut</code>, so we better be careful not to pass it to two functions at
once!
A function like the following cannot work on our <code class="language-plaintext highlighter-rouge">&amp;Foo</code>:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">fn</span> <span class="n">bar</span><span class="o">&lt;</span><span class="n">T</span><span class="p">:</span> <span class="n">Deserialize</span><span class="o">&gt;</span><span class="p">(</span><span class="n">bytes</span><span class="p">:</span> <span class="nb">Vec</span><span class="o">&lt;</span><span class="nb">u8</span><span class="o">&gt;</span><span class="p">)</span> <span class="p">{</span>
    <span class="c1">// The iterator needs to capture the `&amp;mut` context.</span>
    <span class="k">for</span> <span class="n">item</span> <span class="k">in</span> <span class="nf">whatever</span><span class="p">(</span><span class="n">bytes</span><span class="p">)</span><span class="nf">.map</span><span class="p">(|</span><span class="n">x</span><span class="p">|</span> <span class="nn">T</span><span class="p">::</span><span class="nf">deserialize</span><span class="p">(</span><span class="n">x</span><span class="p">))</span> <span class="p">{</span>
        <span class="c1">// Trying to use it here too is an aliasing violation.</span>
        <span class="k">let</span> <span class="n">other_item</span> <span class="o">=</span> <span class="nn">T</span><span class="p">::</span><span class="nf">deserialize</span><span class="p">(</span><span class="nf">something_else</span><span class="p">(</span><span class="n">bytes</span><span class="p">));</span>
        <span class="o">...</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>We therefore need to distinguish the 4 kinds of ownership semantics we can encounter:</p>
<ul>
  <li>Today’s default, with no implicit value at all: <code class="language-plaintext highlighter-rouge">&lt;T: Trait&gt;: const</code><sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">3</a></sup>;</li>
  <li><code class="language-plaintext highlighter-rouge">&amp;Context</code>-like semantics: <code class="language-plaintext highlighter-rouge">&lt;T: Trait&gt;: Copy</code>;</li>
  <li><code class="language-plaintext highlighter-rouge">&amp;mut Context</code>-like semantics: <code class="language-plaintext highlighter-rouge">&lt;T: Trait&gt;: Reborrow</code> (using the <code class="language-plaintext highlighter-rouge">Reborrow</code> trait from <a href="https://github.com/rust-lang/rust-project-goals/issues/399">the
project goal</a>)<sup id="fnref:4" role="doc-noteref"><a href="#fn:4" class="footnote" rel="footnote">4</a></sup>;</li>
  <li><code class="language-plaintext highlighter-rouge">Box&lt;Context&gt;</code>-like semantics: <code class="language-plaintext highlighter-rouge">&lt;T: Trait&gt;</code> can contain anything.</li>
</ul>

<p>You’ll have recognized the similarity with the 4 closure traits <a href="https://github.com/rust-lang/rust/issues/148768"><code class="language-plaintext highlighter-rouge">FnStatic</code></a>,
<code class="language-plaintext highlighter-rouge">Fn</code>, <code class="language-plaintext highlighter-rouge">FnMut</code>, and <code class="language-plaintext highlighter-rouge">FnOnce</code>.</p>

<p>Oh and for the owned case, trait bounds can have significant <code class="language-plaintext highlighter-rouge">Drop</code> if not used :3</p>

<p>This is unhinged enough that I’d propose we just limit contexts to being <code class="language-plaintext highlighter-rouge">Copy</code> and use
interior mutability,
but I suspect some delicious APIs could be cooked with the full expressivity 👀.
Plz share in the comments I wanna see them.</p>

<h3 id="methods-are-closures">Methods are closures</h3>

<p>Speaking of closure traits,
methods are closures now:
in our example <code class="language-plaintext highlighter-rouge">&lt;T as Deserialize&gt;::deserialize</code> makes use of the implicit parameter,
so it cannot be cast to a <code class="language-plaintext highlighter-rouge">fn(D) -&gt; Result&lt;.., ..&gt;</code> function pointer.</p>

<p>Depending on the ownership semantics of <code class="language-plaintext highlighter-rouge">&lt;T as Deserialize&gt;</code>,
its methods will implement the corresponding <code class="language-plaintext highlighter-rouge">Fn*</code> closure trait(s) instead.</p>

<h3 id="scoped-impls">Scoped impls</h3>

<p>As Tyler points out in his blog post,
trait bounds can no longer be taken to be global facts!
Depending on which capabilities are in scope, the same <code class="language-plaintext highlighter-rouge">MyType: Trait</code> may
or may hold.</p>

<p>This is a surprisingly expressive new capability,
especially if we stretch the feature set a bit:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="n">MagicPointer</span><span class="o">&lt;</span><span class="nv">'a</span><span class="o">&gt;</span><span class="p">(</span><span class="n">PhantomData</span><span class="o">&lt;&amp;</span><span class="nv">'a</span> <span class="p">()</span><span class="o">&gt;</span><span class="p">);</span>

<span class="n">capability</span> <span class="n">pointer_target</span><span class="p">;</span>

<span class="c1">// This `Deref` impl is only available when the capability is in scope,</span>
<span class="c1">// and it has a different target type depending on scope!</span>
<span class="k">impl</span><span class="o">&lt;</span><span class="nv">'a</span><span class="o">&gt;</span> <span class="n">Deref</span> <span class="k">for</span> <span class="n">MagicPointer</span><span class="o">&lt;</span><span class="nv">'a</span><span class="o">&gt;</span>
<span class="k">where</span>
    <span class="n">pointer_target</span><span class="p">:</span> <span class="k">impl</span> <span class="nb">Sized</span> <span class="o">+</span> <span class="nv">'a</span> <span class="c1">// Can be basically anything</span>
<span class="p">{</span>
    <span class="k">type</span> <span class="n">Target</span> <span class="o">=</span> <span class="nd">type_of!</span><span class="p">(</span><span class="n">pointer_target</span><span class="p">);</span> <span class="c1">// I cheat, don't tell</span>
    <span class="k">fn</span> <span class="nf">deref</span><span class="p">(</span><span class="o">&amp;</span><span class="k">self</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="o">&amp;</span><span class="k">Self</span><span class="p">::</span><span class="n">Target</span> <span class="p">{</span>
        <span class="o">&amp;</span><span class="n">pointer_target</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>This has far-reaching consequences on how we use traits:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="nf">MyInt</span><span class="p">(</span><span class="nb">u32</span><span class="p">);</span>

<span class="n">capability</span> <span class="n">salt</span><span class="p">;</span>

<span class="c1">// This impl is correct inside a given context. But switching contexts breaks it:</span>
<span class="c1">// two equal values may hash differently in different contexts.</span>
<span class="k">impl</span> <span class="n">Hash</span> <span class="k">for</span> <span class="n">MyInt</span>
<span class="k">where</span>
    <span class="n">salt</span><span class="p">:</span> <span class="nb">u32</span>
<span class="p">{</span>
    <span class="o">...</span> <span class="c1">// hash `self.0.xor(salt)`</span>
<span class="p">}</span>

<span class="k">fn</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">let</span> <span class="k">mut</span> <span class="n">set</span><span class="p">:</span> <span class="n">HashSet</span><span class="o">&lt;</span><span class="n">MyInt</span><span class="o">&gt;</span> <span class="o">=</span> <span class="nn">Default</span><span class="p">::</span><span class="nf">default</span><span class="p">();</span>
    <span class="n">with</span> <span class="n">salt</span> <span class="o">=</span> <span class="mi">42</span> <span class="p">{</span>
        <span class="n">set</span><span class="nf">.insert</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span>
    <span class="p">}</span>
    <span class="n">with</span> <span class="n">salt</span> <span class="o">=</span> <span class="mi">10</span> <span class="p">{</span>
        <span class="k">if</span> <span class="n">set</span><span class="nf">.contains</span><span class="p">(</span><span class="o">&amp;</span><span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
            <span class="c1">// completely not clear whether that's the case.</span>
            <span class="c1">// depends on impl details</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Either the <code class="language-plaintext highlighter-rouge">Hash</code> impl above is deemed invalid (seems likely),
or datastructures like <code class="language-plaintext highlighter-rouge">HashSet</code> would not opt-in to scoped impls.
Either way, this opens up a new dimension of expressivity.</p>

<h2 id="capturing-impls">Capturing impls</h2>

<p>I introduced the article with context/capabilities, but
this is not the only way I can think of to make trait bounds
carry values.
The other one is to have impls capture from their context!</p>

<p>For this, I’ll reuse the idea of <a href="https://smallcultfollowing.com/babysteps/blog/2025/11/21/move-expressions"><code class="language-plaintext highlighter-rouge">move</code>
expressions</a>,
except I prefer to call them <code class="language-plaintext highlighter-rouge">capture</code> expressions.
We’ll also still need a notion of scoped impls, I’ll write that <code class="language-plaintext highlighter-rouge">local impl</code>.</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="n">Context</span><span class="p">;</span>

<span class="k">trait</span> <span class="n">GimmeArena</span> <span class="p">{</span>
    <span class="c1">// The crazy lifetime syntax would mean "borrows from the trait dictionary".</span>
    <span class="k">fn</span> <span class="nf">gimme</span><span class="p">()</span> <span class="k">-&gt;</span> <span class="o">&amp;</span><span class="err">'</span><span class="p">{</span><span class="k">Self</span> <span class="k">as</span> <span class="n">GimmeArena</span><span class="p">}</span> <span class="n">Arena</span><span class="p">;</span>
<span class="p">}</span>

<span class="k">fn</span> <span class="nf">use_arena</span><span class="p">()</span>
<span class="k">where</span>
    <span class="n">Context</span><span class="p">:</span> <span class="n">GimmeArena</span>
<span class="p">{</span>
    <span class="k">let</span> <span class="n">arena</span> <span class="o">=</span> <span class="nn">Context</span><span class="p">::</span><span class="nf">gimme</span><span class="p">();</span>
    <span class="c1">// use the arena</span>
<span class="p">}</span>

<span class="k">fn</span> <span class="nf">foo</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">let</span> <span class="n">arena</span> <span class="o">=</span> <span class="n">Arena</span><span class="p">;</span>

    <span class="n">local</span> <span class="k">impl</span> <span class="n">GimmeArena</span> <span class="k">for</span> <span class="n">Context</span> <span class="p">{</span>
        <span class="k">fn</span> <span class="nf">gimme</span><span class="p">()</span> <span class="k">-&gt;</span> <span class="o">&amp;</span><span class="err">'</span><span class="p">{</span><span class="k">Self</span> <span class="k">as</span> <span class="n">GimmeArena</span><span class="p">}</span> <span class="n">Arena</span> <span class="p">{</span>
            <span class="nf">capture</span><span class="p">(</span><span class="o">&amp;</span><span class="n">arena</span><span class="p">)</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="c1">// In this scope, `Context` implements `GimmeArena`, and the dictionary</span>
    <span class="c1">// carries a reference to the arena.</span>
    <span class="nf">use_arena</span><span class="p">();</span>
<span class="p">}</span>

<span class="k">fn</span> <span class="nf">bar</span><span class="p">()</span> <span class="p">{</span>
    <span class="c1">// Different scope, so we can make another impl.</span>
    <span class="n">local</span> <span class="k">impl</span> <span class="n">GimmeArena</span> <span class="k">for</span> <span class="n">Context</span> <span class="p">{</span>
        <span class="k">fn</span> <span class="nf">gimme</span><span class="p">()</span> <span class="k">-&gt;</span> <span class="o">&amp;</span><span class="err">'</span><span class="p">{</span><span class="k">Self</span> <span class="k">as</span> <span class="n">GimmeArena</span><span class="p">}</span> <span class="n">Arena</span> <span class="p">{</span>
            <span class="o">...</span> <span class="c1">// do something else</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="nf">use_arena</span><span class="p">();</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Feature-wise, this is pretty similar: <code class="language-plaintext highlighter-rouge">where Context: GimmeArena</code> is very close to
<code class="language-plaintext highlighter-rouge">where arena: &amp;'a Arena</code> from before.</p>

<p>This also clashes with another understanding of what a “capturing impl” might be,
namely where the captured values are accessible from the <code class="language-plaintext highlighter-rouge">&amp;mut self</code> argument,
which would allow conveniently defining <code class="language-plaintext highlighter-rouge">Iterator</code>s, visitors etc.</p>

<p>All in all I’m not too sold on this;
I’m showing it because it’s a good illustration that the important
notion either way is data-carrying impls.</p>

<h2 id="conclusion">Conclusion</h2>

<p>I hope I got you excited about capabilities, and/or about trait-bounds-as-values!
What I find compelling is how naturally
“trait bounds carry runtime values”
interacts with the rest of the language.</p>

<p>I’ll see you later for more explorations
of dictionary-passing-style traits.</p>
<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:3" role="doc-endnote">
      <p>See my <a href="https://nadrieril.github.io/blog/2026/03/20/dictionary-passing-style.html">last post</a> <a href="#fnref:3" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:1" role="doc-endnote">
      <p>I’m fudging the difference between types and values here, but I don’t think there’s ambiguity in practice. A more precise way of doing this would be a magic associated type <code class="language-plaintext highlighter-rouge">&lt;T as Deserialize&gt;::capabilities: 'static + Send</code>. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:2" role="doc-endnote">
      <p>Basically “is fully known at compile-time”, so there’s no need to thread any value. That’s very different from <code class="language-plaintext highlighter-rouge">T: const Trait</code> which would mean “its methods can be called at compile-time”. We’ll probably not use such a similar notation, that would be confusing af x) <a href="#fnref:2" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:4" role="doc-endnote">
      <p>Not sure I’m using this trait right, but I understand it as “<code class="language-plaintext highlighter-rouge">Copy</code> but where the new value is borrowck-linked to the previous one”, and we need something along these lines. <a href="#fnref:4" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Nadri</name></author><summary type="html"><![CDATA[In my last post, I showed you how traits behave like passing a bundle of methods between functions, except automatically inferred by the compiler.]]></summary></entry><entry><title type="html">Equality in Dependent Type Theories</title><link href="https://nadrieril.github.io/blog/2026/03/20/dependent-equality.html" rel="alternate" type="text/html" title="Equality in Dependent Type Theories" /><published>2026-03-20T05:39:00+00:00</published><updated>2026-03-20T05:39:00+00:00</updated><id>https://nadrieril.github.io/blog/2026/03/20/dependent-equality</id><content type="html" xml:base="https://nadrieril.github.io/blog/2026/03/20/dependent-equality.html"><![CDATA[<p>A common way to define equality in type theories,
attributed to Per Martin-Löf,
is as follows:
<code class="language-plaintext highlighter-rouge">a == b</code> is a type with two parameters <code class="language-plaintext highlighter-rouge">a</code> and <code class="language-plaintext highlighter-rouge">b</code>,
and it has a single constructor <code class="language-plaintext highlighter-rouge">refl x</code> with type <code class="language-plaintext highlighter-rouge">x == x</code>.</p>

<p>You might find this weird: the two parameters feel a bit useless since they’ll always be the same.
And you’d be right: them being the same is the whole point of this being an equality.
The way this works is that if you need a proof that two types are equal then you just take a <code class="language-plaintext highlighter-rouge">a ==
b</code> a argument.</p>

<p>So what can you do with an <code class="language-plaintext highlighter-rouge">a == b</code>?
My favorite definition is the one from the <a href="https://homotopytypetheory.org/book">HoTT book</a>:
from the definition of the type,
we automatically get a “destructor” function<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup> :</p>
<div class="language-ocaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">transport</span><span class="o">:</span> <span class="p">(</span><span class="n">a</span><span class="o">:</span> <span class="nc">Type</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="p">(</span><span class="n">b</span><span class="o">:</span> <span class="nc">Type</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="p">(</span><span class="n">f</span><span class="o">:</span> <span class="nc">Type</span> <span class="o">-&gt;</span> <span class="nc">Type</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">a</span> <span class="o">==</span> <span class="n">b</span> <span class="o">-&gt;</span> <span class="n">f</span> <span class="n">a</span> <span class="o">-&gt;</span> <span class="n">f</span> <span class="n">b</span>
</code></pre></div></div>

<p>This function says: if <code class="language-plaintext highlighter-rouge">a == b</code>, then I can turn a <code class="language-plaintext highlighter-rouge">f a</code> into a <code class="language-plaintext highlighter-rouge">f b</code> for any function <code class="language-plaintext highlighter-rouge">f</code>.
What I find insanely cute, and what compelled me to write this, is that this single function is
enough to define a reasonable notion of equality.</p>

<p>Here is a proof of symmetry,
in <a href="https://github.com/Nadrieril/dictionary-passing-lambda-calculus/">a suspiciously Rust-looking dependent
lambda-calculus</a>:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="nf">symmetry</span><span class="p">(</span><span class="n">a</span><span class="p">:</span> <span class="n">Type</span><span class="p">,</span> <span class="n">b</span><span class="p">:</span> <span class="n">Type</span><span class="p">,</span> <span class="n">ab</span><span class="p">:</span> <span class="n">a</span> <span class="o">==</span> <span class="n">b</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="n">b</span> <span class="o">==</span> <span class="n">a</span> <span class="o">=</span>
    <span class="n">transport</span> <span class="n">a</span> <span class="nf">b</span> <span class="p">(|</span><span class="n">x</span><span class="p">:</span> <span class="n">Type</span><span class="p">|</span> <span class="n">x</span> <span class="o">==</span> <span class="n">a</span><span class="p">)</span> <span class="nf">ab</span> <span class="p">(</span><span class="n">refl</span> <span class="n">a</span><span class="p">)</span>
</code></pre></div></div>

<p>Here’s how it works: the function being transported is <code class="language-plaintext highlighter-rouge">|x| x == a</code>.
So given <code class="language-plaintext highlighter-rouge">a == b</code>, <code class="language-plaintext highlighter-rouge">transport</code> will turn <code class="language-plaintext highlighter-rouge">a == a</code> into <code class="language-plaintext highlighter-rouge">b == a</code>.
We can build a <code class="language-plaintext highlighter-rouge">a == a</code> using <code class="language-plaintext highlighter-rouge">refl</code>, so we win!</p>

<p>Here is transitivity:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="nf">transitivity</span><span class="p">(</span><span class="n">a</span><span class="p">:</span> <span class="n">Type</span><span class="p">,</span> <span class="n">b</span><span class="p">:</span> <span class="n">Type</span><span class="p">,</span> <span class="n">c</span><span class="p">:</span> <span class="n">Type</span><span class="p">,</span> <span class="n">ab</span><span class="p">:</span> <span class="n">a</span> <span class="o">==</span> <span class="n">b</span><span class="p">,</span> <span class="n">bc</span><span class="p">:</span> <span class="n">b</span> <span class="o">==</span> <span class="n">c</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="n">a</span> <span class="o">==</span> <span class="n">c</span> <span class="o">=</span>
    <span class="p">(</span><span class="n">transport</span> <span class="n">b</span> <span class="nf">c</span> <span class="p">(|</span><span class="n">x</span><span class="p">:</span> <span class="n">Type</span><span class="p">|</span> <span class="n">a</span> <span class="o">==</span> <span class="n">x</span><span class="p">)</span> <span class="n">bc</span><span class="p">)</span> <span class="n">ab</span>
</code></pre></div></div>

<p>The idea is similar: we transport <code class="language-plaintext highlighter-rouge">|x| a == x</code> from <code class="language-plaintext highlighter-rouge">b</code> to <code class="language-plaintext highlighter-rouge">c</code>.</p>

<p>That’s all I had to say, I don’t know why this feels so good to my brain
but now you can taste it too!
If you enjoyed this, go read the <a href="https://homotopytypetheory.org/book">HoTT book</a>.</p>

<!-- Here's for example how one might define the classic `Iterator` and `IntoIterator` traits, -->
<!-- in [a suspiciously Rust-looking dependent -->
<!-- lambda-calculus](https://github.com/Nadrieril/dictionary-passing-lambda-calculus/): -->

<!-- ```rust -->
<!-- // trait Iterator { -->
<!-- //     type Item; -->
<!-- //     fn next(&self) -> Option<Self::Item>; -->
<!-- // } -->
<!-- let Iterator(t: Type) = { -->
<!--     item_ty: Type, -->
<!--     next_method: fn(&t) -> option self.item_ty, -->
<!-- }; -->

<!-- // trait IntoIterator { -->
<!-- //     type Item; -->
<!-- //     type IntoIterator: Iterator<Item = Self::Item>; -->
<!-- // } -->
<!-- let IntoIterator(t: Type) = { -->
<!--     item_ty: Type, -->
<!--     into_iter_ty: Type, -->
<!--     iterator_bound: Iterator(self.into_iter_ty), -->
<!--     // Here we express the equality of the Item types. -->
<!--     type_eq: self.item_ty == self.iterator_bound.item_ty, -->
<!-- }; -->
<!-- ``` -->
<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1" role="doc-endnote">
      <p>Apparently this turns the type into its Church encoding. You may enjoy this related <a href="https://pure.tudelft.nl/ws/portalfiles/portal/83694767/leibniz_equality_is_isomorphic_to_martinlof_identity_parametrically.pdf">fun paper</a>. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Nadri</name></author><summary type="html"><![CDATA[A common way to define equality in type theories, attributed to Per Martin-Löf, is as follows: a == b is a type with two parameters a and b, and it has a single constructor refl x with type x == x.]]></summary></entry><entry><title type="html">Elaborating Rust Traits to Dictionary-Passing Style</title><link href="https://nadrieril.github.io/blog/2026/03/20/dictionary-passing-style.html" rel="alternate" type="text/html" title="Elaborating Rust Traits to Dictionary-Passing Style" /><published>2026-03-20T04:53:00+00:00</published><updated>2026-03-20T04:53:00+00:00</updated><id>https://nadrieril.github.io/blog/2026/03/20/dictionary-passing-style</id><content type="html" xml:base="https://nadrieril.github.io/blog/2026/03/20/dictionary-passing-style.html"><![CDATA[<blockquote>
  <p>This article is part of an collaboration with the Rust Types team where
we’re looking into integrating these ideas into the Rust compiler to make it
more robust and more correct.</p>

  <p>I’m like babby in the realm of trait solving internals, expect this to be directionally right
but missing some important caveats.</p>
</blockquote>

<p>In Rust, traits assign behaviors to types in such a way that when I write <code class="language-plaintext highlighter-rouge">my_val.clone()</code>,
the compiler can figure out the right code to call automatically.</p>

<p>This process is called “trait solving”: given all the traits and all the trait impls found in
the current crate and its dependencies,
the trait solver figures out for each trait reference whether the trait is indeed implemented
for that type,
and if so where to find the required methods/associated types/associated constants.</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">trait</span> <span class="nb">Clone</span> <span class="p">{</span>
    <span class="k">fn</span> <span class="nf">clone</span><span class="p">(</span><span class="o">&amp;</span><span class="k">self</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="k">Self</span><span class="p">;</span>
<span class="p">}</span>

<span class="k">impl</span> <span class="nb">Clone</span> <span class="k">for</span> <span class="nb">u32</span> <span class="p">{</span> <span class="o">...</span> <span class="p">}</span>
<span class="k">impl</span><span class="o">&lt;</span><span class="n">T</span><span class="p">:</span> <span class="nb">Clone</span><span class="o">&gt;</span> <span class="nb">Clone</span> <span class="k">for</span> <span class="nb">Vec</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span> <span class="p">{</span> <span class="o">...</span> <span class="p">}</span>

<span class="k">let</span> <span class="n">my_vec</span><span class="p">:</span> <span class="nb">Vec</span><span class="o">&lt;</span><span class="nb">u32</span><span class="o">&gt;</span> <span class="o">=</span> <span class="nd">vec!</span><span class="p">[</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">];</span>
<span class="n">my_vec</span><span class="nf">.clone</span><span class="p">();</span> <span class="c1">// uses the two impls above together</span>
</code></pre></div></div>

<p>In this article I’d like to flesh out an obvious-in-retrospect idea
that makes it easier to talk about what it is that the trait solver does.</p>

<h2 id="dictionary-passing-style">“Dictionary-Passing Style”</h2>

<p>The idea is simple: traits are like struct definitions<sup id="fnref:4" role="doc-noteref"><a href="#fn:4" class="footnote" rel="footnote">1</a></sup>, impls are like struct values,
and trait bounds are how you pass such structs from one function to the next.
Taking the <code class="language-plaintext highlighter-rouge">Clone</code> example above, we can understand it as:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="nb">Clone</span><span class="o">&lt;</span><span class="k">Self</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="n">clone</span><span class="p">:</span> <span class="k">fn</span><span class="p">(</span><span class="o">&amp;</span><span class="k">Self</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="k">Self</span><span class="p">,</span>
<span class="p">}</span>

<span class="k">const</span> <span class="n">CLONE_U32</span><span class="p">:</span> <span class="nb">Clone</span><span class="o">&lt;</span><span class="nb">u32</span><span class="o">&gt;</span> <span class="o">=</span> <span class="nb">Clone</span> <span class="p">{</span>
    <span class="n">clone</span><span class="p">:</span> <span class="p">|</span><span class="n">x</span><span class="p">:</span> <span class="o">&amp;</span><span class="nb">u32</span><span class="p">|</span> <span class="k">-&gt;</span> <span class="nb">u32</span> <span class="p">{</span> <span class="o">*</span><span class="n">x</span> <span class="p">},</span>
<span class="p">}</span>

<span class="c1">// I'm imagining generic consts because why not. See how the trait bound becomes a</span>
<span class="c1">// const generic argument.</span>
<span class="k">const</span> <span class="n">CLONE_VEC</span><span class="o">&lt;</span><span class="n">T</span><span class="p">,</span> <span class="k">const</span> <span class="n">CLONE_T</span><span class="p">:</span> <span class="nb">Clone</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;&gt;</span><span class="p">:</span> <span class="nb">Clone</span><span class="o">&lt;</span><span class="nb">Vec</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;&gt;</span> <span class="o">=</span> <span class="nb">Clone</span> <span class="p">{</span>
    <span class="n">clone</span><span class="p">:</span> <span class="p">|</span><span class="n">vec</span><span class="p">:</span> <span class="o">&amp;</span><span class="nb">Vec</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span><span class="p">|</span> <span class="k">-&gt;</span> <span class="nb">Vec</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span> <span class="p">{</span> <span class="c1">// Dummy clone impl, it's probably not even that bad</span>
        <span class="n">vec</span>
            <span class="nf">.iter</span><span class="p">()</span>
            <span class="nf">.map</span><span class="p">(|</span><span class="n">x</span><span class="p">:</span> <span class="o">&amp;</span><span class="n">T</span><span class="p">|</span> <span class="n">CLONE_T</span><span class="nf">.clone</span><span class="p">(</span><span class="n">x</span><span class="p">))</span>
            <span class="nf">.collect</span><span class="p">()</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="k">let</span> <span class="n">my_vec</span><span class="p">:</span> <span class="nb">Vec</span><span class="o">&lt;</span><span class="nb">u32</span><span class="o">&gt;</span> <span class="o">=</span> <span class="nd">vec!</span><span class="p">[</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">];</span>
<span class="p">(</span><span class="nn">CLONE_VEC</span><span class="p">::</span><span class="o">&lt;</span><span class="nb">u32</span><span class="p">,</span> <span class="n">CLONE_U32</span><span class="o">&gt;</span><span class="p">)</span><span class="nf">.clone</span><span class="p">(</span><span class="o">&amp;</span><span class="n">my_vec</span><span class="p">);</span>
</code></pre></div></div>

<p>The interesting part is that a trait bound like <code class="language-plaintext highlighter-rouge">where T: Clone</code> becomes
an argument, something that must be provided by the caller.
This reflects trait solving: calling <code class="language-plaintext highlighter-rouge">clone</code> on <code class="language-plaintext highlighter-rouge">Vec&lt;T&gt;</code> requires
the trait solver to figure out whether and why <code class="language-plaintext highlighter-rouge">T: Clone</code>.</p>

<p>With a program in this form, it’s easy to track what code gets called where: we just have to follow
those impl structs as they’re passed down through function calls and other impls.</p>

<blockquote>
  <p>[!NOTE]
While not necessarily involving actual struct values being passed around,
this way of seeing traits/typeclasses/implicit modules is known as “dictionary-passing style”
(at least in <a href="https://okmij.org/ftp/Computation/typeclass.html">the Haskell literature</a>)
so we’re reusing this standard terminology.</p>
</blockquote>

<h2 id="trait-solving-is-elaboration">Trait Solving is Elaboration</h2>

<p>Armed with this metaphor, we can answer the question: “what does trait solving do?”.
The answer: it finds for each trait clause a corresponding trait proof, which
may come from an impl, a trait bound, or other sources I haven’t mentioned yet.</p>

<p>We could imagine a Rust that has syntax to express this<sup id="fnref:3" role="doc-noteref"><a href="#fn:3" class="footnote" rel="footnote">2</a></sup>.
From our example we see that the main ingredients are:</p>
<ul>
  <li>Giving a name to impls,</li>
  <li>Giving a name to trait bounds,</li>
  <li>Passing a trait proof to an item that has a corresponding trait bound.</li>
</ul>

<p>If you’ll allow me to pull a whole bunch of syntax out of my hat:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">trait</span> <span class="nb">Clone</span> <span class="p">{</span>
    <span class="k">fn</span> <span class="nf">clone</span><span class="p">(</span><span class="o">&amp;</span><span class="k">self</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="k">Self</span><span class="p">;</span>
<span class="p">}</span>

<span class="c1">// This syntax gives a name to the impl.</span>
<span class="k">impl</span> <span class="s">"clone_u32"</span> <span class="nb">Clone</span> <span class="k">for</span> <span class="nb">u32</span> <span class="p">{</span> <span class="o">...</span> <span class="p">}</span>

<span class="c1">// This takes two arguments: the explicit `T`, and the implicit `T: Clone`.</span>
<span class="k">impl</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span> <span class="s">"clone_vec"</span> <span class="nb">Clone</span> <span class="k">for</span> <span class="nb">Vec</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span>
<span class="k">where</span>
    <span class="n">clone_t</span><span class="p">:</span> <span class="p">[</span><span class="n">T</span><span class="p">:</span> <span class="nb">Clone</span><span class="p">]</span> <span class="c1">// weird syntax don't @ me</span>
<span class="p">{</span>
    <span class="k">fn</span> <span class="nf">clone</span><span class="p">(</span><span class="o">&amp;</span><span class="k">self</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="k">Self</span> <span class="p">{</span>
        <span class="n">vec</span>
            <span class="nf">.iter</span><span class="p">()</span>
            <span class="c1">// We use the trait bound explicitly here</span>
            <span class="nf">.map</span><span class="p">(|</span><span class="n">x</span><span class="p">:</span> <span class="o">&amp;</span><span class="n">T</span><span class="p">|</span> <span class="nn">clone_t</span><span class="p">::</span><span class="nf">clone</span><span class="p">(</span><span class="n">x</span><span class="p">))</span>
            <span class="nf">.collect</span><span class="p">()</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="k">let</span> <span class="n">my_vec</span><span class="p">:</span> <span class="nb">Vec</span><span class="o">&lt;</span><span class="nb">u32</span><span class="o">&gt;</span> <span class="o">=</span> <span class="nd">vec!</span><span class="p">[</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">];</span>
<span class="c1">// square brackets is my syntax for passing implicit params:</span>
<span class="nn">clone_vec</span><span class="p">::</span><span class="o">&lt;</span><span class="nb">u32</span><span class="o">&gt;</span><span class="p">[</span><span class="n">clone_u32</span><span class="p">]::</span><span class="nf">clone</span><span class="p">(</span><span class="o">&amp;</span><span class="n">my_vec</span><span class="p">);</span>
</code></pre></div></div>

<p>To cover all of Rust we’ll need a few more ingredients, but not that many: naming parent traits,
naming clauses on associated types, associated type equalities (see next section), and not that much
more.</p>

<blockquote>
  <p>[!NOTE]
Another terminology drop: such a process of “filling in implicit information” is
typically called “elaboration” in type theory circles.
We can thus call this process “trait elaboration”.</p>
</blockquote>

<h2 id="the-tricky-part-associated-types-and-equality">The Tricky Part: Associated Types and Equality</h2>

<p>The main ingredient we’re missing is associated types.
Our “dictionaries” can actually contain types.
They definitely aren’t normal structs anymore but that won’t stop us:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">trait</span> <span class="nb">Iterator</span> <span class="p">{</span>
    <span class="k">type</span> <span class="n">Item</span><span class="p">;</span>
    <span class="k">fn</span> <span class="nf">next</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span> <span class="k">self</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nb">Option</span><span class="o">&lt;</span><span class="k">Self</span><span class="p">::</span><span class="n">Item</span><span class="o">&gt;</span><span class="p">;</span>
<span class="p">}</span>
<span class="c1">// behaves like:</span>
<span class="k">struct</span> <span class="nb">Iterator</span><span class="o">&lt;</span><span class="k">Self</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="n">item</span><span class="p">:</span> <span class="n">Type</span><span class="p">,</span> <span class="c1">// look ma, types inside values</span>
    <span class="n">next</span><span class="p">:</span> <span class="k">fn</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span> <span class="k">Self</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nb">Option</span><span class="o">&lt;</span><span class="k">self</span><span class="py">.item</span><span class="o">&gt;</span><span class="p">,</span> <span class="c1">// a little bit of self-reference magic 🤫</span>
<span class="p">}</span>
</code></pre></div></div>

<p>What associated types add to our system is
equality bounds, as in <code class="language-plaintext highlighter-rouge">T: Iterator&lt;Item = u32&gt;</code>.
We can turn these into “passing-style” too, using a neat trick:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// This would be a built-in trait understood by the compiler.</span>
<span class="k">trait</span> <span class="n">Is</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span> <span class="p">{}</span>
<span class="k">impl</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span> <span class="s">"is_impl"</span> <span class="n">Is</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span> <span class="k">for</span> <span class="n">T</span> <span class="p">{}</span>

<span class="c1">// Then an equality bound looks like:</span>
<span class="k">fn</span> <span class="n">foo</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span><span class="p">()</span>
<span class="k">where</span>
    <span class="n">iter_t</span><span class="p">:</span> <span class="p">[</span><span class="n">T</span><span class="p">:</span> <span class="nb">Iterator</span><span class="p">],</span>
    <span class="n">item_is_u32</span><span class="p">:</span> <span class="p">[</span><span class="nn">iter_t</span><span class="p">::</span><span class="n">Item</span><span class="p">:</span> <span class="n">Is</span><span class="o">&lt;</span><span class="nb">u32</span><span class="o">&gt;</span><span class="p">],</span>
<span class="p">{</span> <span class="o">...</span> <span class="p">}</span>
</code></pre></div></div>

<p>Just like a trait bound, the caller of <code class="language-plaintext highlighter-rouge">foo</code> would then have to provide a proof that <code class="language-plaintext highlighter-rouge">T::Item</code> is
indeed <code class="language-plaintext highlighter-rouge">u32</code>.
In the simple case that proof is given by <code class="language-plaintext highlighter-rouge">is_impl</code>, and in more complex
cases we can imagine making things like transitivity of equality available<sup id="fnref:5" role="doc-noteref"><a href="#fn:5" class="footnote" rel="footnote">3</a></sup>.</p>

<p>This topic is really the tricky part of our endeavour.
The trait solver and type checker use type equalities all the time,
so trying to track them explicitly would require a whole bunch of new shenanigans.
But it can, at least in theory, be done.</p>

<h2 id="can-this-even-work">Can this even work?</h2>

<p>There’s a classic <a href="https://github.com/lcnr/random-rust-snippets/issues/2#issuecomment-2426326050">example</a>
that illustrates why dictionary-passing style causes trouble in today’s Rust:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">trait</span> <span class="n">Trait</span> <span class="p">{</span>
    <span class="k">type</span> <span class="n">Item</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">impl</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span> <span class="n">Trait</span> <span class="k">for</span> <span class="n">T</span> <span class="p">{</span>
    <span class="k">type</span> <span class="n">Item</span> <span class="o">=</span> <span class="n">T</span><span class="p">;</span>
<span class="p">}</span>

<span class="k">fn</span> <span class="n">callee</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span><span class="p">(</span><span class="n">t</span><span class="p">:</span> <span class="n">T</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="o">&lt;</span><span class="n">T</span> <span class="k">as</span> <span class="n">Trait</span><span class="o">&gt;</span><span class="p">::</span><span class="n">Item</span> <span class="p">{</span>
    <span class="n">t</span>
<span class="p">}</span>
<span class="k">fn</span> <span class="n">caller</span><span class="o">&lt;</span><span class="n">T</span><span class="p">:</span> <span class="n">Trait</span><span class="o">&lt;</span><span class="n">Item</span> <span class="o">=</span> <span class="n">U</span><span class="o">&gt;</span><span class="p">,</span> <span class="n">U</span><span class="o">&gt;</span><span class="p">(</span><span class="n">t</span><span class="p">:</span> <span class="n">T</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="n">U</span> <span class="p">{</span>
    <span class="c1">// The return type of `callee` is `&lt;T as Trait&gt;::Item` which we know to be `U`</span>
    <span class="c1">// so all looks good.</span>
    <span class="nf">callee</span><span class="p">(</span><span class="n">t</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Let’s elaborate it to understand the trouble:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">trait</span> <span class="n">Trait</span> <span class="p">{</span>
    <span class="k">type</span> <span class="n">Item</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">impl</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span> <span class="s">"the_impl"</span> <span class="n">Trait</span> <span class="k">for</span> <span class="n">T</span> <span class="p">{</span>
    <span class="k">type</span> <span class="n">Item</span> <span class="o">=</span> <span class="n">T</span><span class="p">;</span>
<span class="p">}</span>

<span class="k">fn</span> <span class="n">callee</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span><span class="p">(</span><span class="n">t</span><span class="p">:</span> <span class="n">T</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="n">the_impl</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span><span class="p">::</span><span class="n">Item</span> <span class="p">{</span>
    <span class="n">t</span>
<span class="p">}</span>

<span class="k">fn</span> <span class="n">caller</span><span class="o">&lt;</span><span class="n">T</span><span class="p">,</span> <span class="n">U</span><span class="o">&gt;</span><span class="p">(</span><span class="n">t</span><span class="p">:</span> <span class="n">T</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="n">U</span>
<span class="k">where</span>
    <span class="n">t_trait</span><span class="p">:</span> <span class="p">[</span><span class="n">T</span><span class="p">:</span> <span class="n">Trait</span><span class="p">]</span>
    <span class="n">item_is_u</span><span class="p">:</span> <span class="p">[</span><span class="nn">t_trait</span><span class="p">::</span><span class="n">Item</span><span class="p">:</span> <span class="n">Is</span><span class="o">&lt;</span><span class="n">U</span><span class="o">&gt;</span><span class="p">]</span>
<span class="p">{</span>
    <span class="c1">// ERROR: `the_impl&lt;T&gt;::Item` is `T`, yet we need a `U`.</span>
    <span class="nn">callee</span><span class="p">::</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span><span class="p">(</span><span class="n">t</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>

<p>This code is accepted today, yet to typecheck in dictionary-passing style we would need
to know that <code class="language-plaintext highlighter-rouge">T = U</code>.
A way to understand the problem is that because of the global impl,
<code class="language-plaintext highlighter-rouge">caller</code> can only be called with <code class="language-plaintext highlighter-rouge">T = U</code>. But <code class="language-plaintext highlighter-rouge">caller</code> doesn’t know this:
it lives in a world where <code class="language-plaintext highlighter-rouge">T</code> and <code class="language-plaintext highlighter-rouge">U</code> may be different, yet calls a function
that knows them to be the same.</p>

<p>The reason this works in today’s Rust is “coherence”, i.e. the knowledge that there can be only one
<code class="language-plaintext highlighter-rouge">Trait</code> impl for <code class="language-plaintext highlighter-rouge">T</code>.
It’s not obvious how to fit that into the dictionary approach.</p>

<p>Our answer to this problem is: we may find a clean solution,
or we may find good-enough hacks, or we may even choose to reject such code<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">4</a></sup>,
because dictionaries could just be worth it.</p>

<h2 id="all-this-for-soundness">All This For Soundness</h2>

<p>You may now be asking “why would we be doing all this?”.
The answer is “soundness”<sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">5</a></sup>, in two ways:</p>

<ul>
  <li>From the implementation point of view,
we have high hopes that doing things like this
will avoid a lot of soundness bugs in the compiler.</li>
  <li>From the research point of view,
this could be a way to <em>prove</em> soundness,
i.e. to find sufficient conditions
that ensure (at least) that the trait solver
doesn’t allow safe code to cause UB.</li>
</ul>

<p>With this blog post, we get a first such condition: trait elaboration must produce appropriate proofs.
For example, using a <code class="language-plaintext highlighter-rouge">T: Clone</code> bound where <code class="language-plaintext highlighter-rouge">T: PartialEq</code> is expected is obviously incorrect.</p>

<p>This by itself isn’t enough however.
I’ll take this <a href="https://github.com/rust-lang/rust/issues/135246">known unsoundness</a> as an example
(<a href="https://play.rust-lang.org/?version=stable&amp;mode=debug&amp;edition=2024&amp;gist=53c595e69cf821257ee7f48c80203bd0">playground</a>):</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">trait</span> <span class="n">Trait</span><span class="o">&lt;</span><span class="n">R</span><span class="o">&gt;</span><span class="p">:</span> <span class="nb">Sized</span> <span class="p">{</span>
    <span class="k">type</span> <span class="n">Proof</span><span class="p">:</span> <span class="n">Trait</span><span class="o">&lt;</span><span class="n">R</span><span class="p">,</span> <span class="n">Proof</span> <span class="o">=</span> <span class="k">Self</span><span class="o">&gt;</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">impl</span><span class="o">&lt;</span><span class="n">L</span><span class="p">,</span> <span class="n">R</span><span class="o">&gt;</span> <span class="n">Trait</span><span class="o">&lt;</span><span class="n">R</span><span class="o">&gt;</span> <span class="k">for</span> <span class="n">L</span> <span class="p">{</span>
    <span class="k">type</span> <span class="n">Proof</span>
        <span class="o">=</span> <span class="n">R</span>
    <span class="k">where</span>
        <span class="n">L</span><span class="p">:</span> <span class="n">Trait</span><span class="o">&lt;</span><span class="n">R</span><span class="o">&gt;</span><span class="p">,</span>
        <span class="n">R</span><span class="p">:</span> <span class="n">Trait</span><span class="o">&lt;</span><span class="n">R</span><span class="p">,</span> <span class="n">Proof</span> <span class="o">=</span> <span class="o">&lt;</span><span class="nn">L</span><span class="p">::</span><span class="n">Proof</span> <span class="k">as</span> <span class="n">Trait</span><span class="o">&lt;</span><span class="n">R</span><span class="o">&gt;&gt;</span><span class="p">::</span><span class="n">Proof</span><span class="o">&gt;</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">fn</span> <span class="n">transmute_inner</span><span class="o">&lt;</span><span class="n">L</span><span class="p">:</span> <span class="n">Trait</span><span class="o">&lt;</span><span class="n">R</span><span class="o">&gt;</span><span class="p">,</span> <span class="n">R</span><span class="o">&gt;</span><span class="p">(</span><span class="n">r</span><span class="p">:</span> <span class="n">L</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="o">&lt;</span><span class="nn">L</span><span class="p">::</span><span class="n">Proof</span> <span class="k">as</span> <span class="n">Trait</span><span class="o">&lt;</span><span class="n">R</span><span class="o">&gt;&gt;</span><span class="p">::</span><span class="n">Proof</span> <span class="p">{</span> <span class="n">r</span> <span class="p">}</span>
<span class="k">fn</span> <span class="n">transmute</span><span class="o">&lt;</span><span class="n">L</span><span class="p">,</span> <span class="n">R</span><span class="o">&gt;</span><span class="p">(</span><span class="n">r</span><span class="p">:</span> <span class="n">L</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="n">R</span> <span class="p">{</span> <span class="nn">transmute_inner</span><span class="p">::</span><span class="o">&lt;</span><span class="n">L</span><span class="p">,</span> <span class="n">R</span><span class="o">&gt;</span><span class="p">(</span><span class="n">r</span><span class="p">)</span> <span class="p">}</span> <span class="c1">// oops</span>
</code></pre></div></div>

<p>Here, each individual trait bound and associated type equality can find a justification (I promise).
The issue is that the overall dependency of proofs on each other
is too recursive, so we end up “proving X by assuming X”.</p>

<p>And so we also need a more global notion of “the overall elaborated program isn’t
too ridiculously recursive”.
Defining this properly is what we’re working on at the moment.</p>

<p>One question I don’t know the answer to yet is: are these conditions enough?
Most likely we’d also need a condition related to coherence<sup id="fnref:6" role="doc-noteref"><a href="#fn:6" class="footnote" rel="footnote">6</a></sup> (the fact that there’s at most one impl
for each trait and type);
is that all we’re missing?
I’m looking forward to finding out.</p>

<h2 id="summary">Summary</h2>

<p>Dictionaries are, in my opinion, a very cute and useful way of understanding traits.
They bring the massive benefit of making it much more obvious
to reason about what is or isn’t sound: if you can express it as a dictionary thing,
it’s probably sound.</p>

<p>One example is <a href="https://github.com/rust-lang/rfcs/pull/1210">specialization</a>,
which has been stalled for a decade <a href="https://aturon.github.io/blog/2017/07/08/lifetime-dispatch/">due to
unsoundnesses</a>.
<a href="https://lcnr.de/about/">lcnr</a> writes about a “maybe bounds”
idea <a href="https://lcnr.de/blog/2026/03/06/always-applicable.html">here</a>,
which would be a way to get sound specialization,
and we know it’s sound because it’s trivially dictionary-passing.</p>

<p>I’m hoping we can integrate this into rustc and
advance the grand vision of formally specifying Rust!
Stay tuned for more developments;
you may also follow <a href="https://rust-lang.github.io/rust-project-goals/2026/dictionary-passing-style-experiment.html">our project
goal</a>
when it gets approved.</p>

<p><em>Thanks to lcnr and Boxy teaching me so much about these topics. And particular
thanks to lcnr for mentoring me through this project.</em></p>
<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:4" role="doc-endnote">
      <p>This idea aren’t particularly ludicrous: <code class="language-plaintext highlighter-rouge">dyn Trait</code> is represented at runtime by carrying around a “virtual table”/”vtable”, which is exactly a struct like this with one function pointer per trait method. <a href="#fnref:4" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:3" role="doc-endnote">
      <p>Ohoho, wouldn’t this look like <a href="https://nadrieril.github.io/rust-via-desugarings/">a desugaring</a>? 👀👀 <a href="#fnref:3" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:5" role="doc-endnote">
      <p>Transitivity is easy enough, the annoying part is the substitution property, i.e. that <code class="language-plaintext highlighter-rouge">T: Is&lt;U&gt;</code> implies <code class="language-plaintext highlighter-rouge">Foo&lt;T&gt;: Is&lt;Foo&lt;U&gt;&gt;</code> where <code class="language-plaintext highlighter-rouge">Foo</code> can be any type that depends on <code class="language-plaintext highlighter-rouge">T</code>. I don’t know how I’d represent that in convenient syntax. <a href="#fnref:5" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:1" role="doc-endnote">
      <p>A user may fix this code by adding a <code class="language-plaintext highlighter-rouge">T: Trait&lt;Item = T&gt;</code> constraint so that <code class="language-plaintext highlighter-rouge">caller</code> can know that <code class="language-plaintext highlighter-rouge">T = U</code> <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:2" role="doc-endnote">
      <p>“Soundness” is the technical term for “those checks do ensure that the program will run correctly”. Today trait solving is unsound, as there are known bugs like <a href="https://github.com/rust-lang/rust/issues/135246">this one</a> that allow UB in safe code. <a href="#fnref:2" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:6" role="doc-endnote">
      <p><a href="https://github.com/boxyuwu">Boxy</a> tells me we may not need coherence, I’m looking forward to her blog post on the topic :3 <a href="#fnref:6" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Nadri</name></author><summary type="html"><![CDATA[This article is part of an collaboration with the Rust Types team where we’re looking into integrating these ideas into the Rust compiler to make it more robust and more correct. I’m like babby in the realm of trait solving internals, expect this to be directionally right but missing some important caveats.]]></summary></entry><entry><title type="html">The Algebra of Loans in Rust</title><link href="https://nadrieril.github.io/blog/2025/12/21/the-algebra-of-loans-in-rust.html" rel="alternate" type="text/html" title="The Algebra of Loans in Rust" /><published>2025-12-21T01:49:00+00:00</published><updated>2025-12-21T01:49:00+00:00</updated><id>https://nadrieril.github.io/blog/2025/12/21/the-algebra-of-loans-in-rust</id><content type="html" xml:base="https://nadrieril.github.io/blog/2025/12/21/the-algebra-of-loans-in-rust.html"><![CDATA[<p>The heart of Rust borrow-checking is this: when a borrow is taken, and until it expires, access to
the borrowed place is restricted. For example you may not read from a place while it is mutably
borrowed.</p>

<p>Over the recent months, lively discussions have been happening around teaching the borrow-checker
to understand more things than just “shared borrow”/”mutable borrow”.
We’re starting to have a few ideas floating around, so I thought I put them all down in a table so
we can see how they interact.
I’ll start with the tables and explain the new reference types after.</p>

<p>To clarify the vocabulary I’ll be using:</p>
<ul>
  <li>A “place” is a memory location, represented by an expression like <code class="language-plaintext highlighter-rouge">x</code>, <code class="language-plaintext highlighter-rouge">*x</code>, <code class="language-plaintext highlighter-rouge">x.field</code> etc<sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">1</a></sup>;</li>
  <li>“Taking a borrow” is the action of evaluating <code class="language-plaintext highlighter-rouge">&amp;place</code>, <code class="language-plaintext highlighter-rouge">&amp;mut place</code>, <code class="language-plaintext highlighter-rouge">&amp;own place</code> etc to a value;</li>
  <li>A “loan” happens when we take a borrow of a place. The loan remembers the place that was borrowed
and the kind of reference used to borrow it. When we take that borrow, the resulting reference
type gets a fresh lifetime; as long as that lifetime is present in the type of a value somewhere,
the loan is considered “live”;</li>
  <li>While a loan is live, further operations on the borrowed place are restricted;</li>
  <li>After the loan expires, operations on the borrowed place may be restricted too.</li>
</ul>

<p>Note: These tables are not enough to understand everything about these new references. E.g. after
you write to a <code class="language-plaintext highlighter-rouge">&amp;uninit</code> you can reborrow it as <code class="language-plaintext highlighter-rouge">&amp;own</code>, and vice-versa; this is not reflected in the
tables. Another example: dropping the contents of a place removes any pinning restriction placed on
it.</p>

<h4 id="table-1-given-a-reference-what-actions-are-possible-with-it">Table 1: Given a reference, what actions are possible with it</h4>

<p>How to read this table: the reference I have gives me the column, the action I want to do with it
gives me the row (the reference types mean that the action is a reborrow with that type).</p>

<p>For example, if I have a <code class="language-plaintext highlighter-rouge">&amp;own T</code> I can reborrow it into a <code class="language-plaintext highlighter-rouge">&amp;mut T</code> but not a <code class="language-plaintext highlighter-rouge">&amp;pin mut T</code>. If
I have a <code class="language-plaintext highlighter-rouge">&amp;mut T</code> I may write a new value to it but not drop its contents.</p>

<p>“Move out” means “move the value out without putting a new one in”. “Drop” means “run the drop code
in-place”.</p>

<table>
  <thead>
    <tr>
      <th style="text-align: right"> </th>
      <th>&amp;</th>
      <th>&amp;mut</th>
      <th>&amp;own</th>
      <th>&amp;pin</th>
      <th>&amp;pin mut</th>
      <th>&amp;pin own</th>
      <th>&amp;uninit</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: right"><strong>&amp;</strong></td>
      <td>✅</td>
      <td>✅</td>
      <td>✅</td>
      <td>✅</td>
      <td>✅</td>
      <td>✅</td>
      <td>❌</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>&amp;mut</strong></td>
      <td>❌</td>
      <td>✅</td>
      <td>✅</td>
      <td>❌</td>
      <td>❌</td>
      <td>❌</td>
      <td>❌</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>&amp;own</strong></td>
      <td>❌</td>
      <td>❌</td>
      <td>✅</td>
      <td>❌</td>
      <td>❌</td>
      <td>❌</td>
      <td>❌</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>&amp;pin</strong></td>
      <td>❌</td>
      <td>❌</td>
      <td>❌</td>
      <td>✅</td>
      <td>✅</td>
      <td>✅</td>
      <td>❌</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>&amp;pin mut</strong></td>
      <td>❌</td>
      <td>❌</td>
      <td>❌</td>
      <td>❌</td>
      <td>✅</td>
      <td>✅</td>
      <td>❌</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>&amp;pin own</strong></td>
      <td>❌</td>
      <td>❌</td>
      <td>✅</td>
      <td>❌</td>
      <td>❌</td>
      <td>✅</td>
      <td>❌</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>&amp;uninit</strong></td>
      <td>❌</td>
      <td>❌</td>
      <td>❌</td>
      <td>❌</td>
      <td>❌</td>
      <td>❌</td>
      <td>✅</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Read</strong></td>
      <td>✅</td>
      <td>✅</td>
      <td>✅</td>
      <td>✅</td>
      <td>✅</td>
      <td>✅</td>
      <td>❌</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Write</strong></td>
      <td>❌</td>
      <td>✅</td>
      <td>✅</td>
      <td>❌</td>
      <td>✅</td>
      <td>✅</td>
      <td>✅</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Move out</strong></td>
      <td>❌</td>
      <td>❌</td>
      <td>✅</td>
      <td>❌</td>
      <td>❌</td>
      <td>❌</td>
      <td>❌</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Drop</strong></td>
      <td>❌</td>
      <td>❌</td>
      <td>✅</td>
      <td>❌</td>
      <td>❌</td>
      <td>✅</td>
      <td>❌</td>
    </tr>
  </tbody>
</table>

<h4 id="table-2-if-a-loan-was-taken-and-is-live-what-can-i-still-do-to-the-place">Table 2: If a loan was taken and is live, what can I still do to the place</h4>

<p>How to read this table: a borrow of place <code class="language-plaintext highlighter-rouge">p</code> was taken and is still live; the kind of borrow gives
me the column. The operations I may still do on the place (without going through that borrow) give
me the row.</p>

<p>For example, if a <code class="language-plaintext highlighter-rouge">&amp;</code>-borrow was taken and is live, I may still read the place.</p>

<table>
  <thead>
    <tr>
      <th style="text-align: right"> </th>
      <th>&amp;</th>
      <th>&amp;mut</th>
      <th>&amp;own</th>
      <th>&amp;pin</th>
      <th>&amp;pin mut</th>
      <th>&amp;pin own</th>
      <th>&amp;uninit</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: right"><strong>&amp;</strong></td>
      <td>✅</td>
      <td>❌</td>
      <td>❌</td>
      <td>✅</td>
      <td>❌</td>
      <td>❌</td>
      <td>❌</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>&amp;mut</strong></td>
      <td>❌</td>
      <td>❌</td>
      <td>❌</td>
      <td>❌</td>
      <td>❌</td>
      <td>❌</td>
      <td>❌</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>&amp;own</strong></td>
      <td>❌</td>
      <td>❌</td>
      <td>❌</td>
      <td>❌</td>
      <td>❌</td>
      <td>❌</td>
      <td>❌</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>&amp;pin</strong></td>
      <td>✅</td>
      <td>❌</td>
      <td>❌</td>
      <td>✅</td>
      <td>❌</td>
      <td>❌</td>
      <td>❌</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>&amp;pin mut</strong></td>
      <td>❌</td>
      <td>❌</td>
      <td>❌</td>
      <td>❌</td>
      <td>❌</td>
      <td>❌</td>
      <td>❌</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>&amp;pin own</strong></td>
      <td>❌</td>
      <td>❌</td>
      <td>❌</td>
      <td>❌</td>
      <td>❌</td>
      <td>❌</td>
      <td>❌</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>&amp;uninit</strong></td>
      <td>❌</td>
      <td>❌</td>
      <td>❌</td>
      <td>❌</td>
      <td>❌</td>
      <td>❌</td>
      <td>❌</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Read</strong></td>
      <td>✅</td>
      <td>❌</td>
      <td>❌</td>
      <td>✅</td>
      <td>❌</td>
      <td>❌</td>
      <td>❌</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Write</strong></td>
      <td>❌</td>
      <td>❌</td>
      <td>❌</td>
      <td>❌</td>
      <td>❌</td>
      <td>❌</td>
      <td>❌</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Move out</strong></td>
      <td>❌</td>
      <td>❌</td>
      <td>❌</td>
      <td>❌</td>
      <td>❌</td>
      <td>❌</td>
      <td>❌</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Drop</strong></td>
      <td>❌</td>
      <td>❌</td>
      <td>❌</td>
      <td>❌</td>
      <td>❌</td>
      <td>❌</td>
      <td>❌</td>
    </tr>
  </tbody>
</table>

<p>Unsurprisingly, most of the time we can’t do anything else to the place, because the borrow is
exclusive.</p>

<h4 id="table-3-if-a-loan-was-taken-and-expired-what-can-i-now-do-to-the-place">Table 3: If a loan was taken and expired, what can I now do to the place</h4>

<p>How to read this table: a borrow of place <code class="language-plaintext highlighter-rouge">p</code> was taken and has expired; the kind of borrow gives me
the column. The operations I may now do on the place give me the row.</p>

<p>For example, if a <code class="language-plaintext highlighter-rouge">&amp;own</code>-borrow was taken and expired, I may no longer read the place.</p>

<table>
  <thead>
    <tr>
      <th style="text-align: right"> </th>
      <th>&amp;</th>
      <th>&amp;mut</th>
      <th>&amp;own</th>
      <th>&amp;pin</th>
      <th>&amp;pin mut</th>
      <th>&amp;pin own</th>
      <th>&amp;uninit</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: right"><strong>&amp;</strong></td>
      <td>✅</td>
      <td>✅</td>
      <td>❌</td>
      <td>✅</td>
      <td>✅</td>
      <td>❌</td>
      <td>❌</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>&amp;mut</strong></td>
      <td>✅</td>
      <td>✅</td>
      <td>❌</td>
      <td>❌</td>
      <td>❌</td>
      <td>❌</td>
      <td>❌</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>&amp;own</strong></td>
      <td>✅</td>
      <td>✅</td>
      <td>❌</td>
      <td>❌</td>
      <td>❌</td>
      <td>❌</td>
      <td>❌</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>&amp;pin</strong></td>
      <td>✅</td>
      <td>✅</td>
      <td>❌</td>
      <td>✅</td>
      <td>✅</td>
      <td>❌</td>
      <td>❌</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>&amp;pin mut</strong></td>
      <td>✅</td>
      <td>✅</td>
      <td>❌</td>
      <td>✅</td>
      <td>✅</td>
      <td>❌</td>
      <td>❌</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>&amp;pin own</strong></td>
      <td>✅</td>
      <td>✅</td>
      <td>❌</td>
      <td>✅</td>
      <td>✅</td>
      <td>❌</td>
      <td>❌</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>&amp;uninit</strong></td>
      <td>❌</td>
      <td>❌</td>
      <td>✅</td>
      <td>❌</td>
      <td>❌</td>
      <td>✅</td>
      <td>✅</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Read</strong></td>
      <td>✅</td>
      <td>✅</td>
      <td>❌</td>
      <td>✅</td>
      <td>✅</td>
      <td>❌</td>
      <td>❌</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Write</strong></td>
      <td>✅</td>
      <td>✅</td>
      <td>✅</td>
      <td>✅</td>
      <td>✅</td>
      <td>✅</td>
      <td>✅</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Move out</strong></td>
      <td>✅</td>
      <td>✅</td>
      <td>❌</td>
      <td>❌</td>
      <td>❌</td>
      <td>❌</td>
      <td>❌</td>
    </tr>
    <tr>
      <td style="text-align: right"><strong>Drop</strong></td>
      <td>✅</td>
      <td>✅</td>
      <td>❌</td>
      <td>✅</td>
      <td>✅</td>
      <td>❌</td>
      <td>❌</td>
    </tr>
  </tbody>
</table>

<p><code class="language-plaintext highlighter-rouge">&amp;own</code> and <code class="language-plaintext highlighter-rouge">&amp;uninit</code> both have the property that when they expire the place is considered to have
been uninitialized. So we the only actions available are those that work on uninitialized places:
writing and <code class="language-plaintext highlighter-rouge">&amp;uninit</code> borrows.</p>

<p>The other point to note are the pinning loans: after a pinning loan expires, non-pinning mutable
loans can’t be taken of that same place until the value is dropped.</p>

<h2 id="whats-with-all-these-new-reference-types">What’s with all these new reference types?</h2>

<p>All of these are speculative ideas, but at this point they’ve been circulating a bunch so should be
pretty robust.</p>

<h3 id="the-owning-reference-own-t">The owning reference <code class="language-plaintext highlighter-rouge">&amp;own T</code></h3>

<p><code class="language-plaintext highlighter-rouge">&amp;own T</code><sup id="fnref:4" role="doc-noteref"><a href="#fn:4" class="footnote" rel="footnote">2</a></sup> (which has also been called <code class="language-plaintext highlighter-rouge">&amp;move T</code>) is a reference that says “I have full ownership over
this value, in particular I am responsible for dropping it”. It feels like <code class="language-plaintext highlighter-rouge">Box</code> except that we
don’t control the allocation the value resides in. In particular, we can move the <code class="language-plaintext highlighter-rouge">T</code> out of a <code class="language-plaintext highlighter-rouge">&amp;own
T</code>.</p>

<p>Like <code class="language-plaintext highlighter-rouge">Box</code>, <code class="language-plaintext highlighter-rouge">&amp;own T</code> drops the contained value when it is dropped.</p>

<p>When I owning-borrow <code class="language-plaintext highlighter-rouge">&amp;own x</code> a place, I have given up full ownership of the value, and thus must
assume that the place is uninitialized when the borrow expires.</p>

<p>It makes for some interesting APIs:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">impl</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span> <span class="nb">Vec</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="c1">// Pop the last value and return a reference to it (instead of moving it out directly).</span>
    <span class="k">fn</span> <span class="nf">pop_own</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span> <span class="k">self</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nb">Option</span><span class="o">&lt;&amp;</span><span class="n">own</span> <span class="n">T</span><span class="o">&gt;</span> <span class="p">{</span> <span class="o">..</span> <span class="p">}</span>
    <span class="c1">// Iterate over the contained values, emptying the Vec as we go.</span>
    <span class="k">fn</span> <span class="nf">drain_own</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span> <span class="k">self</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="k">impl</span> <span class="nb">Iterator</span><span class="o">&lt;</span><span class="n">Item</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">own</span> <span class="n">T</span><span class="o">&gt;</span> <span class="p">{</span> <span class="o">..</span> <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="the-uninitialized-reference-uninit-t">The uninitialized reference <code class="language-plaintext highlighter-rouge">&amp;uninit T</code></h3>

<p><code class="language-plaintext highlighter-rouge">&amp;uninit T</code><sup id="fnref:5" role="doc-noteref"><a href="#fn:5" class="footnote" rel="footnote">3</a></sup> (which has also been called <code class="language-plaintext highlighter-rouge">&amp;out T</code>) is a reference to an allocated but
not-yet-initialized location. Much like when we do <code class="language-plaintext highlighter-rouge">let x;</code>, the only thing one can do with that
reference is write to it. Once written to, it can be reborrowed into everything we want.</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Typical usage is to initialize a value:</span>
<span class="k">impl</span> <span class="n">MyType</span> <span class="p">{</span>
    <span class="k">fn</span> <span class="nf">init</span><span class="p">(</span><span class="o">&amp;</span><span class="n">uninit</span> <span class="k">self</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="o">&amp;</span><span class="n">own</span> <span class="k">Self</span> <span class="p">{</span>
        <span class="o">*</span><span class="k">self</span> <span class="o">=</span> <span class="nf">new_value</span><span class="p">();</span>
        <span class="o">&amp;</span><span class="n">own</span> <span class="o">*</span><span class="k">self</span>
    <span class="p">}</span>
<span class="p">}</span>
<span class="k">let</span> <span class="n">x</span><span class="p">:</span> <span class="n">MyType</span><span class="p">;</span>
<span class="k">let</span> <span class="n">ptr</span><span class="p">:</span> <span class="o">&amp;</span><span class="n">own</span> <span class="n">MyType</span> <span class="o">=</span> <span class="nn">MyType</span><span class="p">::</span><span class="nf">init</span><span class="p">(</span><span class="o">&amp;</span><span class="n">uninit</span> <span class="n">x</span><span class="p">);</span>
<span class="c1">// `ptr` can be used much like a `Box` would. It cannot be returned from the</span>
<span class="c1">// current function though.</span>
</code></pre></div></div>

<p>It has a nice synergy with <code class="language-plaintext highlighter-rouge">&amp;own T</code>: we can get from <code class="language-plaintext highlighter-rouge">&amp;uninit T</code> to <code class="language-plaintext highlighter-rouge">&amp;own T</code> by writing a value into
the reference, and get from <code class="language-plaintext highlighter-rouge">&amp;own T</code> to <code class="language-plaintext highlighter-rouge">&amp;uninit T</code> by moving the value out of the reference.</p>

<p>Both have the property that when they expire, the original place is considered uninitialized<sup id="fnref:3" role="doc-noteref"><a href="#fn:3" class="footnote" rel="footnote">4</a></sup>.</p>

<h3 id="the-pinning-references-pin-tpin-mut-tpin-own-t">The pinning references <code class="language-plaintext highlighter-rouge">&amp;pin T</code>/<code class="language-plaintext highlighter-rouge">&amp;pin mut T</code>/<code class="language-plaintext highlighter-rouge">&amp;pin own T</code></h3>

<blockquote>
  <p>Pinning is a notoriously subtle notion, if you’re not familiar with it I recommand <a href="https://doc.rust-lang.org/std/pin/index.html">the std docs
on the topic</a>. If you are familiar with it you may
instead enjoy <a href="https://nadrieril.github.io/blog/2025/11/12/pinning-is-a-kind-of-static-borrow.html">this blog post of
mine</a> that
shines an original light on the notion.</p>
</blockquote>

<p>Pinning references are variants of the existing reference types that also add a “pinning
requirement” to the borrowed place. This requirement forbids moving the value out or deallocating
the place without running <code class="language-plaintext highlighter-rouge">Drop</code> on the value first. This applies even after the pinning borrow has
expired.</p>

<p><code class="language-plaintext highlighter-rouge">&amp;pin mut T</code><sup id="fnref:6" role="doc-noteref"><a href="#fn:6" class="footnote" rel="footnote">5</a></sup> is the most common of these, it exists in today’s Rust as <code class="language-plaintext highlighter-rouge">Pin&lt;&amp;mut T&gt;</code> and is crucial
to our async story. <code class="language-plaintext highlighter-rouge">&amp;pin T</code> would be <code class="language-plaintext highlighter-rouge">Pin&lt;&amp;T&gt;</code>; it’s less clear how useful it is but I can imagine
usecases.</p>

<p><code class="language-plaintext highlighter-rouge">&amp;pin own T</code> finally would be the owning variant. This one has the tricky requirement that it must
not be passed to <code class="language-plaintext highlighter-rouge">mem::forget</code> as that would break the drop invariant. This isn’t possible in
today’s Rust, but there have been proposals over the years as such non-forgettable types are needed
for other things.</p>

<p>One funky aspect of <code class="language-plaintext highlighter-rouge">&amp;pin own T</code> is that I think it’s ok to reborrow a <code class="language-plaintext highlighter-rouge">&amp;own T</code> into a <code class="language-plaintext highlighter-rouge">&amp;pin own T</code>:
since the only way for the borrow to expire entails dropping the pointed-to value, there’s no way to
break the pin guarantee so it doesn’t matter how we got that reference.</p>

<p>You’ll notice I didn’t list <code class="language-plaintext highlighter-rouge">&amp;pin uninit T</code>. That’s because pinning is a property of a value, and
<code class="language-plaintext highlighter-rouge">&amp;pin uninit T</code> doesn’t point to a value. To pin-initialize a value, one can just write the value to
a <code class="language-plaintext highlighter-rouge">&amp;uninit T</code> then reborrow <code class="language-plaintext highlighter-rouge">&amp;uninit T -&gt; &amp;own T -&gt; &amp;pin own T</code>.</p>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:2" role="doc-endnote">
      <p>You can find more details in <a href="https://nadrieril.github.io/blog/2025/12/06/on-places-and-their-magic.html">my blog post on the topic</a>. <a href="#fnref:2" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:4" role="doc-endnote">
      <p>It’s been proposed a number of times; my go-to writeup on the topic is <a href="https://internals.rust-lang.org/t/a-sketch-for-move-semantics/18632/19?u=illicitonion">this one</a> by Daniel Henry-Mantilla. <a href="#fnref:4" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:5" role="doc-endnote">
      <p>That’s also been proposed a few times. I present a rather tame take on it, proposals are typically more featureful. The one I’m most aware of is <a href="https://hackmd.io/@rust-for-linux-/H11r2RXpgl">this one</a> by the in-place-init working group. <a href="#fnref:5" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:3" role="doc-endnote">
      <p>There are <a href="https://hackmd.io/@rust-for-linux-/H11r2RXpgl#Notable-features">proposals</a> that would allow in the code above to use the <code class="language-plaintext highlighter-rouge">&amp;own MyType</code> to prove to the borrowck that <code class="language-plaintext highlighter-rouge">x</code> is now initialized, but we won’t go into that. <a href="#fnref:3" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:6" role="doc-endnote">
      <p>While pinning has existed in Rust for a couple years now, there’s now <a href="https://github.com/rust-lang/rust/issues/130494">a push</a> to integrate it into the language properly, and this is where I take that syntax from. <a href="#fnref:6" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Nadri</name></author><summary type="html"><![CDATA[The heart of Rust borrow-checking is this: when a borrow is taken, and until it expires, access to the borrowed place is restricted. For example you may not read from a place while it is mutably borrowed.]]></summary></entry><entry><title type="html">Specifying Rust via Desugarings</title><link href="https://nadrieril.github.io/blog/2025/12/18/specifying-rust-via-desugarings.html" rel="alternate" type="text/html" title="Specifying Rust via Desugarings" /><published>2025-12-18T20:48:00+00:00</published><updated>2025-12-18T20:48:00+00:00</updated><id>https://nadrieril.github.io/blog/2025/12/18/specifying-rust-via-desugarings</id><content type="html" xml:base="https://nadrieril.github.io/blog/2025/12/18/specifying-rust-via-desugarings.html"><![CDATA[<blockquote>
  <p>In a recent meeting, <a href="https://github.com/nikomatsakis">Niko</a> proposed that we should add language
features to enable desugaring more implicit things into valid Rust. This blog post is me taking his
idea and running with it.</p>

  <p>Edit: I have turned this idea into a <a href="https://nadrieril.github.io/rust-via-desugarings">book</a>! This book
describes ~30 desugaring steps that go from surface Rust to a MIR-like subset of it. I intend this to
be a reference document or even a spec kind of thing. Do have a look! I came up with even more
language features to make it work.</p>
</blockquote>

<p>How does one specify a language like Rust? At the base of understanding a Rust program today, we
have the <a href="https://rustc-dev-guide.rust-lang.org/mir/index.html">MIR</a>. It’s a representation of
a program as a control-flow graph (i.e. with gotos between blocks) and basic operations like
“compute <code class="language-plaintext highlighter-rouge">a+b</code> and store it in <code class="language-plaintext highlighter-rouge">c</code>” or “call this function with these arguments”.</p>

<p>MIR is simple enough that its semantics can be formally described, and in fact the
<a href="https://github.com/minirust/minirust">MiniRust</a> project aims to do just that. So, given a MIR
program, we can<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup> know exactly what it means to run it, and all the rest of the compilation to
machine code is but an implementation detail.</p>

<p>The question then is how we get from a Rust program to its MIR. Today a lot of that is hard to
explain; the idea of this blog post is that we would like to explain it as a desugaring, into code
that is also valid Rust yet precise enough that the mapping from it to MIR is obvious<sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">2</a></sup>.</p>

<p>What features are we missing to make this desugaring possible? Here are some ideas. (Disclaimer:
none of these are my ideas<sup id="fnref:8" role="doc-noteref"><a href="#fn:8" class="footnote" rel="footnote">3</a></sup>, but instead things I’ve seen floating around in the Rust ideaspace).</p>

<h2 id="identifier-hygiene">Identifier hygiene</h2>

<p>Macros have <a href="https://doc.rust-lang.org/reference/macros-by-example.html?#hygiene">hygiene</a>, which
avoids accidentally shadowing names:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
<span class="nd">macro_rules!</span> <span class="n">check</span> <span class="p">{</span>
    <span class="p">()</span> <span class="k">=&gt;</span> <span class="p">{</span> <span class="nd">assert_eq!</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span> <span class="p">};</span> <span class="c1">// Uses `x` from the definition site.</span>
<span class="p">}</span>
<span class="k">let</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span>
<span class="nd">check!</span><span class="p">();</span> <span class="c1">// the test passes</span>
<span class="nd">println!</span><span class="p">(</span><span class="s">"{x}"</span><span class="p">)</span> <span class="c1">// prints 2</span>
</code></pre></div></div>

<p>To desugar that to valid rust code, we would need to make this information explicit<sup id="fnref:3" role="doc-noteref"><a href="#fn:3" class="footnote" rel="footnote">4</a></sup>, maybe by
renaming identifiers or providing extra disambiguation information:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="n">x</span><span class="err">#</span><span class="mi">1</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
<span class="k">let</span> <span class="n">x</span><span class="err">#</span><span class="mi">2</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span>
<span class="nd">assert_eq!</span><span class="p">(</span><span class="n">x</span>#<span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span> <span class="c1">// the test passes</span>
<span class="nd">println!</span><span class="p">(</span><span class="s">"{x#2}"</span><span class="p">)</span> <span class="c1">// prints 2</span>
</code></pre></div></div>

<h2 id="non-dropping-assignment">Non-dropping assignment</h2>

<p>The assignment <code class="language-plaintext highlighter-rouge">*x = y;</code> does two things: it drops the previous value inside <code class="language-plaintext highlighter-rouge">*x</code>, then writes <code class="language-plaintext highlighter-rouge">y</code>
to it. To desugar this we need to make the drop explicit, but then we need something new to express
“an assignment that doesn’t drop first”.</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">*</span><span class="n">x</span> <span class="o">=</span> <span class="n">y</span><span class="p">;</span>
<span class="c1">// desugars to:</span>
<span class="nf">drop</span><span class="p">(</span><span class="o">*</span><span class="n">x</span><span class="p">);</span>
<span class="o">*</span><span class="n">x</span> <span class="o">???</span> <span class="n">y</span><span class="p">;</span> <span class="c1">// would need a special assignment operator that doesn't drop first</span>
</code></pre></div></div>

<p>One possible solution would be for the borrow-checker to track that <code class="language-plaintext highlighter-rouge">*x</code> has been moved out and
therefore <code class="language-plaintext highlighter-rouge">*x = y;</code> no longer needs to drop the value. This already happens if we had a local
variable instead of <code class="language-plaintext highlighter-rouge">*x</code>, so why not. The trouble is that determining whether a drop is required is
non-trivial, which is in tension with our goal of “the mapping to MIR is obvious”.</p>

<h2 id="enum-projections">Enum projections</h2>

<p>In this example, we’re taking a borrow inside an enum:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="n">opt</span><span class="p">:</span> <span class="o">&amp;</span><span class="k">mut</span> <span class="nb">Option</span><span class="o">&lt;</span><span class="nb">u32</span><span class="o">&gt;</span> <span class="o">=</span> <span class="o">...</span><span class="p">;</span>
<span class="k">if</span> <span class="k">let</span> <span class="nf">Some</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="o">=</span> <span class="n">opt</span> <span class="p">{</span>
    <span class="c1">// x: &amp;mut u32 here</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The meaning of this is simple: we first check the enum variant, then borrow the right subplace.
We’re only missing syntax for that subplace:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="n">opt</span><span class="p">:</span> <span class="o">&amp;</span><span class="k">mut</span> <span class="nb">Option</span><span class="o">&lt;</span><span class="nb">u32</span><span class="o">&gt;</span> <span class="o">=</span> <span class="o">...</span><span class="p">;</span>
<span class="k">let</span> <span class="n">discriminant</span> <span class="o">=</span> <span class="nn">std</span><span class="p">::</span><span class="nn">mem</span><span class="p">::</span><span class="nf">discriminant</span><span class="p">(</span><span class="o">&amp;*</span><span class="n">opt</span><span class="p">);</span>
<span class="k">if</span> <span class="n">discriminant</span> <span class="o">==</span> <span class="nd">discriminant_of!</span><span class="p">(</span><span class="nb">Option</span><span class="o">&lt;</span><span class="nb">u32</span><span class="o">&gt;</span><span class="p">,</span> <span class="nb">Some</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">let</span> <span class="n">x</span> <span class="o">=</span> <span class="k">unsafe</span> <span class="p">{</span> <span class="o">&amp;</span><span class="k">mut</span> <span class="p">(</span><span class="o">*</span><span class="n">opt</span><span class="p">)</span><span class="py">.Some</span><span class="na">.0</span> <span class="p">};</span>
<span class="p">}</span>
</code></pre></div></div>

<p>I’d propose <code class="language-plaintext highlighter-rouge">&lt;place&gt;.&lt;variant_name&gt;</code> as a syntax, and that would be an unsafe operation because you
must have checked that the variant is the right one first.</p>

<h2 id="phased-initialization">Phased initialization</h2>

<p>How does one construct a new value? Built-in values have their built-in constructors (<code class="language-plaintext highlighter-rouge">42</code>, <code class="language-plaintext highlighter-rouge">true</code>,
<code class="language-plaintext highlighter-rouge">67.5</code>, <code class="language-plaintext highlighter-rouge">&amp;mut x</code>). For structs, enums and unions we have constructors like <code class="language-plaintext highlighter-rouge">Struct { field: 42 }</code>,
which can be desugared into assignments to the individual fields. Borrowck could easily figure out
that the value is initialized once all the fields are written to<sup id="fnref:3:1" role="doc-noteref"><a href="#fn:3" class="footnote" rel="footnote">4</a></sup> :</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="n">x</span> <span class="o">=</span> <span class="n">Struct</span> <span class="p">{</span> <span class="n">field</span><span class="p">:</span> <span class="mi">42</span> <span class="p">};</span>
<span class="c1">// would desugar to:</span>
<span class="k">let</span> <span class="n">x</span><span class="p">:</span> <span class="n">Struct</span><span class="p">;</span>
<span class="n">x</span><span class="py">.field</span> <span class="o">=</span> <span class="mi">42</span><span class="p">;</span>
<span class="c1">// Here `x` can be used normally</span>
</code></pre></div></div>

<p>For enums we can use the enum projections above, combined with a way to set the discriminant (I
quite like <a href="https://github.com/rust-lang/rfcs/pull/3607">this syntax</a> combined with <a href="https://github.com/rust-lang/rfcs/pull/3727">these
semantics</a>:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="n">x</span><span class="p">:</span> <span class="nb">Option</span><span class="o">&lt;</span><span class="nb">u32</span><span class="o">&gt;</span><span class="p">;</span>
<span class="k">unsafe</span> <span class="p">{</span>
    <span class="n">x</span><span class="py">.Some</span><span class="na">.0</span> <span class="o">=</span> <span class="mi">42</span><span class="p">;</span>
    <span class="n">x</span><span class="py">.enum</span><span class="err">#</span><span class="n">discriminant</span> <span class="o">=</span> <span class="nd">discriminant_of!</span><span class="p">(</span><span class="nb">Option</span><span class="o">&lt;</span><span class="nb">u32</span><span class="o">&gt;</span><span class="p">,</span> <span class="nb">Some</span><span class="p">));</span>
    <span class="c1">// The discriminant is only allowed to be set once the rest of the fields are</span>
    <span class="c1">// initialized, so borrowck can use that to know that `x` is initialized now.</span>
<span class="p">}</span>
</code></pre></div></div>

<p>I wonder if we could allow this in safe code somehow.</p>

<h2 id="naming-the-return-place">Naming the return place</h2>

<p>Not strictly necessary but fun: the expression <code class="language-plaintext highlighter-rouge">return x</code> mixes two things: a control-flow construct
and setting the return value. We could separate the two by making the return place nameable:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">fn</span> <span class="nf">foo</span><span class="p">()</span> <span class="k">-&gt;</span> <span class="nb">u32</span> <span class="p">{</span>
    <span class="k">if</span> <span class="nf">check</span><span class="p">()</span> <span class="p">{</span>
        <span class="k">return</span> <span class="mi">42</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="mi">0</span>
<span class="p">}</span>
<span class="c1">// would become:</span>
<span class="k">fn</span> <span class="nf">foo</span><span class="p">()</span> <span class="k">-&gt;</span> <span class="nb">u32</span> <span class="p">{</span>
    <span class="k">if</span> <span class="nf">check</span><span class="p">()</span> <span class="p">{</span>
        <span class="k">return</span><span class="err">#</span><span class="n">place</span> <span class="o">=</span> <span class="mi">42</span><span class="p">;</span>
        <span class="k">return</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="k">return</span><span class="err">#</span><span class="n">place</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>No idea of a good syntax here. The way I imagine this working is that the return place starts out
uninitialized, like <code class="language-plaintext highlighter-rouge">let x;</code>, and a plain <code class="language-plaintext highlighter-rouge">return</code> is allowed if borrowck determines that the return
place has been initialized.</p>

<h2 id="explicit-pointer-metadata-and-explicit-vtables">Explicit pointer metadata and explicit vtables</h2>

<p>Pointer metadata for unsized types is handled invisibly today:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">trait</span> <span class="n">Trait</span> <span class="p">{</span>
    <span class="k">fn</span> <span class="nf">method</span><span class="p">(</span><span class="o">&amp;</span><span class="k">self</span><span class="p">);</span>
<span class="p">}</span>

<span class="k">struct</span> <span class="n">Struct</span><span class="p">;</span>
<span class="k">impl</span> <span class="n">Trait</span> <span class="k">for</span> <span class="n">Struct</span> <span class="p">{</span>
    <span class="k">fn</span> <span class="nf">method</span><span class="p">(</span><span class="o">&amp;</span><span class="k">self</span><span class="p">)</span> <span class="p">{</span>
        <span class="nd">println!</span><span class="p">(</span><span class="s">"hi!"</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="k">let</span> <span class="n">x</span><span class="p">:</span> <span class="n">Struct</span> <span class="o">=</span> <span class="o">...</span><span class="p">;</span>
<span class="k">let</span> <span class="n">x</span><span class="p">:</span> <span class="o">&amp;</span><span class="k">dyn</span> <span class="n">Trait</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">x</span><span class="p">;</span> <span class="c1">// now the pointer carries a pointer to the right vtable.</span>
</code></pre></div></div>

<p>We could make that explicit in two parts: making metadata explicit, and making vtables explicit:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="n">TraitVTable</span> <span class="p">{</span>
    <span class="n">method</span><span class="p">:</span> <span class="k">unsafe</span> <span class="k">fn</span><span class="p">(</span><span class="o">&amp;</span><span class="k">dyn</span> <span class="n">Trait</span><span class="p">),</span>
<span class="p">}</span>

<span class="k">static</span> <span class="n">ImplTraitForStructVTable</span><span class="p">:</span> <span class="n">TraitVTable</span> <span class="o">=</span> <span class="n">TraitVTable</span> <span class="p">{</span>
    <span class="c1">// Safety: only call if `this` actually points to a `Struct`.</span>
    <span class="n">method</span><span class="p">:</span> <span class="p">|</span><span class="n">this</span><span class="p">:</span> <span class="o">&amp;</span><span class="k">dyn</span> <span class="n">Trait</span><span class="p">|</span> <span class="p">{</span>
        <span class="k">let</span> <span class="n">this</span><span class="p">:</span> <span class="o">&amp;</span><span class="n">Struct</span> <span class="o">=</span> <span class="k">unsafe</span> <span class="p">{</span> <span class="nf">transmute</span><span class="p">(</span><span class="n">this</span><span class="p">)</span> <span class="p">};</span>
        <span class="n">this</span><span class="nf">.method</span><span class="p">()</span>
    <span class="p">}</span>
<span class="p">};</span>

<span class="c1">// Assuming a method like `std::ptr::from_raw_parts` but for references:</span>
<span class="k">let</span> <span class="n">x</span><span class="p">:</span> <span class="o">&amp;</span><span class="k">dyn</span> <span class="n">Trait</span> <span class="o">=</span> <span class="k">unsafe</span> <span class="p">{</span> <span class="nn">std</span><span class="p">::</span><span class="k">ref</span><span class="p">::</span><span class="nf">from_raw_parts</span><span class="p">(</span><span class="o">&amp;</span><span class="n">x</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">ImplTraitForStructVTable</span><span class="p">)</span> <span class="p">};</span>
</code></pre></div></div>

<p>How does this extend to e.g. <code class="language-plaintext highlighter-rouge">Rc&lt;Struct&gt; -&gt; Rc&lt;dyn Trait&gt;</code>? Unclear, one option would be to add
a method to the (unstable) <code class="language-plaintext highlighter-rouge">CoerceUnsize</code> trait that would contain auto-generated code that unpacks
the pointer, adds the vtable metadata, and repacks it.</p>

<h2 id="continue-operator-for-matches"><code class="language-plaintext highlighter-rouge">continue</code> operator for matches</h2>

<p>Operationally, pattern matching expressions just stand for a series of comparisons of discriminants
or integers. So in principle we could desugar a <code class="language-plaintext highlighter-rouge">match</code> to a big series of <code class="language-plaintext highlighter-rouge">if</code>s. However that would not
give us the same MIR as we get today: the lowering of patterns to MIR is a bit sophisticated<sup id="fnref:6" role="doc-noteref"><a href="#fn:6" class="footnote" rel="footnote">5</a></sup>, to
emit more performant code. Let’s try to preserve that.</p>

<p>Let’s start with match guards:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">match</span> <span class="n">opt</span> <span class="p">{</span>
    <span class="nf">Some</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="k">if</span> <span class="nf">foo</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="k">=&gt;</span> <span class="o">..</span><span class="p">,</span>
    <span class="nb">None</span> <span class="k">=&gt;</span> <span class="o">..</span><span class="p">,</span>
    <span class="nf">Some</span><span class="p">(</span><span class="n">_</span><span class="p">)</span> <span class="k">=&gt;</span> <span class="o">..</span><span class="p">,</span>
<span class="p">}</span>
</code></pre></div></div>
<p>In that match, if <code class="language-plaintext highlighter-rouge">foo(x)</code> returns <code class="language-plaintext highlighter-rouge">false</code> we keep trying the arms below. We could give a syntax for
that:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">'a</span><span class="p">:</span> <span class="k">match</span> <span class="n">opt</span> <span class="p">{</span>
    <span class="nf">Some</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="k">=&gt;</span> <span class="k">if</span> <span class="nf">foo</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="p">{</span>
        <span class="o">..</span>
    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
        <span class="k">continue</span> <span class="nv">'a</span><span class="p">;</span> <span class="c1">// tries the arms after this one</span>
    <span class="p">},</span>
    <span class="nb">None</span> <span class="k">=&gt;</span> <span class="o">..</span><span class="p">,</span>
    <span class="nf">Some</span><span class="p">(</span><span class="n">_</span><span class="p">)</span> <span class="k">=&gt;</span> <span class="o">..</span><span class="p">,</span>
<span class="p">}</span>
</code></pre></div></div>
<p>This is interestingly not more expressive than today’s matches, since all of that code could have
just been in the match guard<sup id="fnref:4" role="doc-noteref"><a href="#fn:4" class="footnote" rel="footnote">6</a></sup>. Match exhaustiveness would simply ignore arms that use <code class="language-plaintext highlighter-rouge">continue</code>,
just like it ignores arms with a guard today.</p>

<p>For more complex matches, we can reuse <code class="language-plaintext highlighter-rouge">continue</code> to decompose them into layers:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">match</span> <span class="n">val</span> <span class="p">{</span>
    <span class="p">(</span><span class="nf">Some</span><span class="p">(</span><span class="n">x</span><span class="p">),</span> <span class="nb">None</span><span class="p">)</span> <span class="k">=&gt;</span> <span class="o">..</span><span class="p">,</span> <span class="c1">// branch 1</span>
    <span class="p">(</span><span class="n">_</span><span class="p">,</span> <span class="nf">Some</span><span class="p">(</span><span class="n">y</span><span class="p">))</span> <span class="k">=&gt;</span> <span class="o">..</span><span class="p">,</span> <span class="c1">// branch 2</span>
    <span class="p">(</span><span class="nb">None</span><span class="p">,</span> <span class="nb">None</span><span class="p">)</span> <span class="k">=&gt;</span> <span class="o">..</span><span class="p">,</span> <span class="c1">// branch 3</span>
<span class="p">}</span>
<span class="c1">// could desugar to:</span>
<span class="c1">// This shape might look weird but that's how this `match` expression is compiled to</span>
<span class="c1">// MIR today. It has that shape to avoid duplicating branch 2.</span>
<span class="nv">'a</span><span class="p">:</span> <span class="k">match</span> <span class="n">val</span><span class="na">.0</span><span class="py">.enum</span><span class="err">#</span><span class="n">discriminant</span> <span class="p">{</span>
    <span class="nd">discriminant_of!</span><span class="p">(</span><span class="nb">Option</span><span class="p">,</span> <span class="nb">Some</span><span class="p">)</span> <span class="k">=&gt;</span> <span class="k">match</span> <span class="n">val</span><span class="na">.1</span><span class="py">.enum</span><span class="err">#</span><span class="n">discriminant</span> <span class="p">{</span>
        <span class="nd">discriminant_of!</span><span class="p">(</span><span class="nb">Option</span><span class="p">,</span> <span class="nb">None</span><span class="p">)</span> <span class="k">=&gt;</span> <span class="p">{</span>
            <span class="k">let</span> <span class="n">x</span> <span class="o">=</span> <span class="n">val</span><span class="na">.0</span><span class="py">.Some</span><span class="na">.1</span><span class="p">;</span>
            <span class="c1">// branch 1</span>
        <span class="p">}</span>
        <span class="nd">discriminant_of!</span><span class="p">(</span><span class="nb">Option</span><span class="p">,</span> <span class="nb">Some</span><span class="p">)</span> <span class="k">=&gt;</span> <span class="k">continue</span> <span class="nv">'a</span><span class="p">,</span>
    <span class="p">},</span>
    <span class="n">_</span> <span class="k">=&gt;</span> <span class="k">match</span> <span class="n">val</span><span class="na">.1</span><span class="py">.enum</span><span class="err">#</span><span class="n">discriminant</span> <span class="p">{</span>
        <span class="nd">discriminant_of!</span><span class="p">(</span><span class="nb">Option</span><span class="p">,</span> <span class="nb">Some</span><span class="p">)</span> <span class="k">=&gt;</span> <span class="p">{</span>
            <span class="k">let</span> <span class="n">y</span> <span class="o">=</span> <span class="n">val</span><span class="na">.1</span><span class="py">.Some</span><span class="na">.1</span><span class="p">;</span>
            <span class="c1">// branch 2</span>
        <span class="p">}</span>
        <span class="nd">discriminant_of!</span><span class="p">(</span><span class="nb">Option</span><span class="p">,</span> <span class="nb">None</span><span class="p">)</span> <span class="k">=&gt;</span> <span class="k">match</span> <span class="n">val</span><span class="na">.0</span><span class="py">.enum</span><span class="err">#</span><span class="n">discriminant</span> <span class="p">{</span>
            <span class="nd">discriminant_of!</span><span class="p">(</span><span class="nb">Option</span><span class="p">,</span> <span class="nb">None</span><span class="p">)</span> <span class="k">=&gt;</span> <span class="o">..</span><span class="p">,</span> <span class="c1">// branch 3</span>
            <span class="n">_</span> <span class="k">=&gt;</span> <span class="k">unsafe</span> <span class="p">{</span> <span class="nf">unreachable_unchecked</span><span class="p">()</span> <span class="p">},</span>
        <span class="p">},</span>
    <span class="p">},</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Can we desugar all matches that way? In principle yes because we can do arbitrary<sup id="fnref:7" role="doc-noteref"><a href="#fn:7" class="footnote" rel="footnote">7</a></sup> control-flow using
<code class="language-plaintext highlighter-rouge">break</code>, but that can get ugly:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">match</span> <span class="n">val</span> <span class="p">{</span>
    <span class="p">(</span><span class="nf">Some</span><span class="p">(</span><span class="n">x</span><span class="p">),</span> <span class="nb">None</span><span class="p">)</span> <span class="p">|</span> <span class="p">(</span><span class="nb">None</span><span class="p">,</span> <span class="nf">Some</span><span class="p">(</span><span class="n">x</span><span class="p">))</span> <span class="k">=&gt;</span> <span class="o">..</span><span class="p">,</span> <span class="c1">// branch 1</span>
    <span class="p">(</span><span class="nf">Some</span><span class="p">(</span><span class="n">_</span><span class="p">),</span> <span class="nf">Some</span><span class="p">(</span><span class="n">_</span><span class="p">))</span> <span class="p">|</span> <span class="p">(</span><span class="nb">None</span><span class="p">,</span> <span class="nb">None</span><span class="p">)</span> <span class="k">=&gt;</span> <span class="o">..</span><span class="p">,</span> <span class="c1">// branch 2</span>
<span class="p">}</span>
<span class="c1">// naively desugars to:</span>
<span class="k">match</span> <span class="n">val</span><span class="na">.0</span><span class="py">.enum</span><span class="err">#</span><span class="n">discriminant</span> <span class="p">{</span>
    <span class="nd">discriminant_of!</span><span class="p">(</span><span class="nb">Option</span><span class="p">,</span> <span class="nb">Some</span><span class="p">)</span> <span class="k">=&gt;</span> <span class="k">match</span> <span class="n">val</span><span class="na">.1</span><span class="py">.enum</span><span class="err">#</span><span class="n">discriminant</span> <span class="p">{</span>
        <span class="nd">discriminant_of!</span><span class="p">(</span><span class="nb">Option</span><span class="p">,</span> <span class="nb">None</span><span class="p">)</span> <span class="k">=&gt;</span> <span class="p">{</span>
            <span class="k">let</span> <span class="n">x</span> <span class="o">=</span> <span class="n">val</span><span class="na">.0</span><span class="py">.Some</span><span class="na">.0</span><span class="p">;</span>
            <span class="c1">// branch 1</span>
        <span class="p">}</span>
        <span class="nd">discriminant_of!</span><span class="p">(</span><span class="nb">Option</span><span class="p">,</span> <span class="nb">Some</span><span class="p">)</span> <span class="k">=&gt;</span> <span class="o">..</span><span class="p">,</span> <span class="c1">// branch 2</span>
    <span class="p">},</span>
    <span class="nd">discriminant_of!</span><span class="p">(</span><span class="nb">Option</span><span class="p">,</span> <span class="nb">None</span><span class="p">)</span> <span class="k">=&gt;</span> <span class="k">match</span> <span class="n">val</span><span class="na">.1</span><span class="py">.enum</span><span class="err">#</span><span class="n">discriminant</span> <span class="p">{</span>
        <span class="nd">discriminant_of!</span><span class="p">(</span><span class="nb">Option</span><span class="p">,</span> <span class="nb">Some</span><span class="p">)</span> <span class="k">=&gt;</span> <span class="p">{</span>
            <span class="k">let</span> <span class="n">x</span> <span class="o">=</span> <span class="n">val</span><span class="na">.1</span><span class="py">.Some</span><span class="na">.0</span><span class="p">;</span>
            <span class="c1">// also branch 1</span>
        <span class="p">}</span>
        <span class="nd">discriminant_of!</span><span class="p">(</span><span class="nb">Option</span><span class="p">,</span> <span class="nb">None</span><span class="p">)</span> <span class="k">=&gt;</span> <span class="o">..</span><span class="p">,</span> <span class="c1">// also branch 2</span>
    <span class="p">},</span>
<span class="p">}</span>
<span class="c1">// to avoid duplication, could do something like:</span>
<span class="nv">'match_end</span><span class="p">:</span> <span class="p">{</span>
    <span class="nv">'branch1</span><span class="p">:</span> <span class="p">{</span>
        <span class="nv">'branch2</span><span class="p">:</span> <span class="p">{</span>
            <span class="k">match</span> <span class="n">val</span><span class="na">.0</span><span class="py">.enum</span><span class="err">#</span><span class="n">discriminant</span> <span class="p">{</span>
                <span class="nd">discriminant_of!</span><span class="p">(</span><span class="nb">Option</span><span class="p">,</span> <span class="nb">Some</span><span class="p">)</span> <span class="k">=&gt;</span> <span class="k">match</span> <span class="n">val</span><span class="na">.1</span><span class="py">.enum</span><span class="err">#</span><span class="n">discriminant</span> <span class="p">{</span>
                    <span class="nd">discriminant_of!</span><span class="p">(</span><span class="nb">Option</span><span class="p">,</span> <span class="nb">None</span><span class="p">)</span> <span class="k">=&gt;</span> <span class="p">{</span>
                        <span class="k">let</span> <span class="n">x</span> <span class="o">=</span> <span class="n">val</span><span class="na">.0</span><span class="py">.Some</span><span class="na">.0</span><span class="p">;</span>
                        <span class="k">break</span> <span class="nv">'branch1</span><span class="p">;</span>
                    <span class="p">}</span>
                    <span class="nd">discriminant_of!</span><span class="p">(</span><span class="nb">Option</span><span class="p">,</span> <span class="nb">Some</span><span class="p">)</span> <span class="k">=&gt;</span> <span class="k">break</span> <span class="nv">'branch2</span><span class="p">,</span>
                <span class="p">},</span>
                <span class="nd">discriminant_of!</span><span class="p">(</span><span class="nb">Option</span><span class="p">,</span> <span class="nb">None</span><span class="p">)</span> <span class="k">=&gt;</span> <span class="k">match</span> <span class="n">val</span><span class="na">.1</span><span class="py">.enum</span><span class="err">#</span><span class="n">discriminant</span> <span class="p">{</span>
                    <span class="nd">discriminant_of!</span><span class="p">(</span><span class="nb">Option</span><span class="p">,</span> <span class="nb">Some</span><span class="p">)</span> <span class="k">=&gt;</span> <span class="p">{</span>
                        <span class="k">let</span> <span class="n">x</span> <span class="o">=</span> <span class="n">val</span><span class="na">.1</span><span class="py">.Some</span><span class="na">.0</span><span class="p">;</span>
                        <span class="k">break</span> <span class="nv">'branch1</span><span class="p">;</span>
                    <span class="p">}</span>
                    <span class="nd">discriminant_of!</span><span class="p">(</span><span class="nb">Option</span><span class="p">,</span> <span class="nb">None</span><span class="p">)</span> <span class="k">=&gt;</span> <span class="k">break</span> <span class="nv">'branch2</span><span class="p">,</span>
                <span class="p">},</span>
            <span class="p">}</span>
        <span class="p">}</span>
        <span class="c1">// code for branch 2</span>
        <span class="k">break</span> <span class="nv">'match_end</span><span class="p">;</span>
    <span class="p">}</span> 
    <span class="c1">// code for branch 1</span>
    <span class="k">break</span> <span class="nv">'match_end</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>I don’t have great ideas here.</p>

<h2 id="unchecked-builtin-indexing">Unchecked builtin indexing</h2>

<p>As we know, Rust inserts bound checks when accessing arrays/slices. On first approximation, we might
desugar like this:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="n">slice</span><span class="p">:</span> <span class="o">&amp;</span><span class="k">mut</span> <span class="p">[</span><span class="n">T</span><span class="p">]</span> <span class="o">=</span> <span class="o">...</span><span class="p">;</span>
<span class="k">let</span> <span class="n">x</span> <span class="o">=</span> <span class="o">&amp;</span><span class="k">mut</span> <span class="n">slice</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span>
<span class="c1">// becomes</span>
<span class="nd">assert!</span><span class="p">(</span><span class="n">slice</span><span class="nf">.len</span><span class="p">()</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">);</span>
<span class="k">let</span> <span class="n">x</span> <span class="o">=</span> <span class="k">unsafe</span> <span class="p">{</span> <span class="n">slice</span><span class="nf">.get_unchecked_mut</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span> <span class="p">};</span>
</code></pre></div></div>

<p>However, a little known fact is that indexing is somewhat understood by the borrow-checker:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="n">slice</span><span class="p">:</span> <span class="o">&amp;</span><span class="k">mut</span> <span class="p">[(</span><span class="n">A</span><span class="p">,</span> <span class="n">B</span><span class="p">)]</span> <span class="o">=</span> <span class="o">...</span><span class="p">;</span>
<span class="k">let</span> <span class="n">x</span> <span class="o">=</span> <span class="o">&amp;</span><span class="k">mut</span> <span class="n">slice</span><span class="p">[</span><span class="n">i</span><span class="p">]</span><span class="na">.0</span><span class="p">;</span>
<span class="k">let</span> <span class="n">y</span> <span class="o">=</span> <span class="o">&amp;</span><span class="k">mut</span> <span class="n">slice</span><span class="p">[</span><span class="n">j</span><span class="p">]</span><span class="na">.1</span><span class="p">;</span>
<span class="nf">do_something</span><span class="p">(</span><span class="n">x</span><span class="p">);</span>
<span class="nf">do_something</span><span class="p">(</span><span class="n">y</span><span class="p">);</span>
</code></pre></div></div>

<p>This is allowed because regardless of <code class="language-plaintext highlighter-rouge">i</code> and <code class="language-plaintext highlighter-rouge">j</code> the borrows are guaranteed to be disjoint. But if
we go through a function call like <code class="language-plaintext highlighter-rouge">get_unchecked_mut</code>, we need full exclusivity of the whole of
<code class="language-plaintext highlighter-rouge">slice</code>! So if we want to preserve this behavior, we need a built-in operator for unchecked
indexing (that the borrow-checker understands).</p>

<h2 id="constant-borrowck-tracked-indexing">Constant, borrowck-tracked, indexing</h2>

<p>To continue on the interactions between borrow-checking and indexing, you might believe that the
borrow-checker never takes into account indices: it refuses to borrow <code class="language-plaintext highlighter-rouge">&amp;mut slice[i]</code> and <code class="language-plaintext highlighter-rouge">&amp;mut
slice[j]</code> simultaneously even when <code class="language-plaintext highlighter-rouge">i</code> and <code class="language-plaintext highlighter-rouge">j</code> are <code class="language-plaintext highlighter-rouge">0</code> and <code class="language-plaintext highlighter-rouge">1</code> which are obviously distinct.</p>

<p>However there is a funky little corner of Rust semantics where it does track indices, namely slice patterns:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="n">slice</span><span class="p">:</span> <span class="o">&amp;</span><span class="k">mut</span> <span class="p">[</span><span class="n">T</span><span class="p">]</span> <span class="o">=</span> <span class="o">...</span><span class="p">;</span>
<span class="k">if</span> <span class="k">let</span> <span class="p">[</span><span class="n">a</span><span class="p">,</span> <span class="o">..</span><span class="p">,</span> <span class="n">b</span><span class="p">]</span> <span class="o">=</span> <span class="n">slice</span>
    <span class="o">&amp;&amp;</span> <span class="k">let</span> <span class="p">[</span><span class="n">_</span><span class="p">,</span> <span class="n">c</span><span class="p">,</span> <span class="n">e</span> <span class="o">@</span> <span class="o">..</span><span class="p">,</span> <span class="n">d</span><span class="p">,</span> <span class="n">_</span><span class="p">]</span> <span class="o">=</span> <span class="n">slice</span> <span class="p">{</span>
    <span class="c1">// a,b,c,d: &amp;mut T</span>
    <span class="c1">// e: &amp;mut [T]</span>
<span class="p">}</span>
<span class="c1">// compiles to, morally:</span>
<span class="k">if</span> <span class="n">slice</span><span class="nf">.len</span><span class="p">()</span> <span class="o">&gt;=</span> <span class="mi">2</span> <span class="o">&amp;&amp;</span> <span class="n">slice</span><span class="nf">.len</span><span class="p">()</span> <span class="o">&gt;=</span> <span class="mi">4</span> <span class="p">{</span>
    <span class="k">let</span> <span class="n">a</span> <span class="o">=</span> <span class="o">&amp;</span><span class="k">mut</span> <span class="n">slice</span><span class="p">[</span><span class="k">const</span> <span class="mi">0</span><span class="p">];</span>
    <span class="k">let</span> <span class="n">b</span> <span class="o">=</span> <span class="o">&amp;</span><span class="k">mut</span> <span class="n">slice</span><span class="p">[</span><span class="k">const</span> <span class="o">-</span><span class="mi">1</span><span class="p">];</span>
    <span class="k">let</span> <span class="n">c</span> <span class="o">=</span> <span class="o">&amp;</span><span class="k">mut</span> <span class="n">slice</span><span class="p">[</span><span class="k">const</span> <span class="mi">1</span><span class="p">];</span>
    <span class="k">let</span> <span class="n">d</span> <span class="o">=</span> <span class="o">&amp;</span><span class="k">mut</span> <span class="n">slice</span><span class="p">[</span><span class="k">const</span> <span class="o">-</span><span class="mi">2</span><span class="p">];</span>
    <span class="k">let</span> <span class="n">e</span> <span class="o">=</span> <span class="o">&amp;</span><span class="k">mut</span> <span class="n">slice</span><span class="p">[</span><span class="k">const</span> <span class="mi">2</span><span class="o">..</span><span class="k">const</span> <span class="o">-</span><span class="mi">2</span><span class="p">];</span>
<span class="p">}</span>
</code></pre></div></div>

<p>It can tell that these are distinct because they use a special constant-indexing primitive in MIR.
The reason for the notation I chose is because it supports also indices that are
constant-starting-from-the-end, which are needed here. I used python-like negative indices for
illustration.</p>

<p>If we wanted to desugar that, we’d need a syntax for these.</p>

<h2 id="in-place-drop">In-place drop</h2>

<p>At the end of a scope, all the live locals and temporaries are automatically dropped. To desugar
that, one could naively want to insert calls to <code class="language-plaintext highlighter-rouge">drop(local)</code>, but that’s actually incorrect: drop
actually happens in-place, which is soundness-critical for pinned types.</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span>
    <span class="k">let</span> <span class="n">x</span> <span class="o">=</span> <span class="nn">String</span><span class="p">::</span><span class="nf">new</span><span class="p">();</span>
<span class="p">}</span>
<span class="c1">// would want to desugar to:</span>
<span class="p">{</span>
    <span class="k">let</span> <span class="n">x</span> <span class="o">=</span> <span class="nn">String</span><span class="p">::</span><span class="nf">new</span><span class="p">();</span>
    <span class="k">unsafe</span> <span class="p">{</span> <span class="nn">std</span><span class="p">::</span><span class="nn">ptr</span><span class="p">::</span><span class="nf">drop_in_place</span><span class="p">(</span><span class="o">&amp;</span><span class="n">raw</span> <span class="k">mut</span> <span class="n">x</span><span class="p">);</span> <span class="p">}</span>
    <span class="c1">// But then borrowck will try to drop the place again!</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The issue is that we also need to tell borrowck “this place is dropped now, don’t try to drop it
again”. Today I think <code class="language-plaintext highlighter-rouge">mem::forget(place)</code> works, but if we get ever get a feature for pinned places
that would not be allowed. In that case we’ll need “in-place mem::forget” or something along these
lines.</p>

<h2 id="etc">Etc</h2>

<p>What I would love to see eventually is a <code class="language-plaintext highlighter-rouge">cargo desugar</code> command<sup id="fnref:5" role="doc-noteref"><a href="#fn:5" class="footnote" rel="footnote">8</a></sup>, or a code action in my editor,
that shows the fully desugared version of a piece of code. Then we’d make the Reference describe
these desugarings, we’d make Miri run on this subset of Rust instead of on MIR, and understanding
the behavior of a piece of Rust code would become much easier!</p>

<p>That’s a bunch of ideas, there a likely a lot of other implicit transformations that can’t be
expressed in plain Rust. Please share your ideas!</p>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1" role="doc-endnote">
      <p>MiniRust is not normative yet so that’s not strictly true, but most likely something like it will become normative eventually. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:2" role="doc-endnote">
      <p>The Rust Reference could then describe these desugarings and formally specify the desugared subset of Rust, instead of needing a different representation like MIR. <a href="#fnref:2" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:8" role="doc-endnote">
      <p>Except the match-<code class="language-plaintext highlighter-rouge">continue</code> statement, that one I came up with on my own :3 <a href="#fnref:8" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:3" role="doc-endnote">
      <p>I’m sure I recall an RFC/pre-RFC for that but I can’t find it, plz let me know if you do. <a href="#fnref:3" class="reversefootnote" role="doc-backlink">&#8617;</a> <a href="#fnref:3:1" class="reversefootnote" role="doc-backlink">&#8617;<sup>2</sup></a></p>
    </li>
    <li id="fn:6" role="doc-endnote">
      <p>It’s not that sophisticated actually, e.g. on the second example below it could figure out that matching on <code class="language-plaintext highlighter-rouge">val.1</code> first produces better code. But we don’t do that kind of reasoning yet. <a href="#fnref:6" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:4" role="doc-endnote">
      <p>Well, we do put restrictions on match guards, in particular they may not change the scrutinee expression. We’d need to make that restriction explicit as part of the desugaring but I don’t know how exactly. <a href="#fnref:4" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:7" role="doc-endnote">
      <p>Arbitrary <a href="https://en.wikipedia.org/wiki/Control-flow_graph#Reducibility">reducible</a> control-flow I should say. <a href="#fnref:7" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:5" role="doc-endnote">
      <p>I found a <a href="https://github.com/mre/cargo-inspect"><code class="language-plaintext highlighter-rouge">cargo inspect</code></a> tool that does some of that! From the other end, my job project <a href="https://github.com/AeneasVerif/charon">Charon</a> takes MIR and reconstructs that kind of simple code, for purposes of analysis. <a href="#fnref:5" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Nadri</name></author><summary type="html"><![CDATA[In a recent meeting, Niko proposed that we should add language features to enable desugaring more implicit things into valid Rust. This blog post is me taking his idea and running with it. Edit: I have turned this idea into a book! This book describes ~30 desugaring steps that go from surface Rust to a MIR-like subset of it. I intend this to be a reference document or even a spec kind of thing. Do have a look! I came up with even more language features to make it work.]]></summary></entry><entry><title type="html">Autoref and Autoderef for First-Class Smart Pointers</title><link href="https://nadrieril.github.io/blog/2025/12/18/autoref-and-autoderef-for-first-class-smart-pointers.html" rel="alternate" type="text/html" title="Autoref and Autoderef for First-Class Smart Pointers" /><published>2025-12-18T01:18:00+00:00</published><updated>2025-12-18T01:18:00+00:00</updated><id>https://nadrieril.github.io/blog/2025/12/18/autoref-and-autoderef-for-first-class-smart-pointers</id><content type="html" xml:base="https://nadrieril.github.io/blog/2025/12/18/autoref-and-autoderef-for-first-class-smart-pointers.html"><![CDATA[<blockquote>
  <p>This blog post is part of the discussions around the <a href="https://github.com/rust-lang/rust-project-goals/issues/390">Field Projections project
goal</a>. Thanks to Benno Lossin and
everyone involved for the very fruitful discussions!</p>
</blockquote>

<p>In a <a href="https://nadrieril.github.io/blog/2025/11/11/truly-first-class-custom-smart-pointers.html">my first post on this blog</a> I outlined a solution for making custom smart pointers as well
integrated into the language as references are today. I had left the exact rules for autoref and
autoderef unspecified; this blog post is my attempt to write them down precisely.</p>

<p>The basic tenets I have for the whole feature are:</p>
<ul>
  <li>Expressions have a type that doesn’t depend on their context.</li>
  <li>To understand what operation (e.g. method call) is being done on an expression, one only needs to know
the type of that expression.</li>
</ul>

<h2 id="placewrap-and-non-indirected-place-operations"><code class="language-plaintext highlighter-rouge">PlaceWrap</code> and non-indirected place operations</h2>

<p>One of the recent ideas we’ve added to the proposal is this trait<sup id="fnref:5" role="doc-noteref"><a href="#fn:5" class="footnote" rel="footnote">1</a></sup>, which we’ll need to explain
the desugarings:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cd">/// If `X: PlaceWrap` and `X::Target` has a field `field`, then `X` itself acquires a virtual field</span>
<span class="cd">/// named `field` as well. That field has type `&lt;X as</span>
<span class="cd">/// PlaceWrap&lt;proj_ty!(X::Target.field)&gt;&gt;::Wrapped`, and `WrappedProj` is the</span>
<span class="cd">/// projection used when we refer to that field.</span>
<span class="k">pub</span> <span class="k">unsafe</span> <span class="k">trait</span> <span class="n">PlaceWrap</span><span class="o">&lt;</span><span class="n">P</span><span class="p">:</span> <span class="n">Projection</span><span class="o">&lt;</span><span class="n">Source</span> <span class="o">=</span> <span class="k">Self</span><span class="p">::</span><span class="n">Target</span><span class="o">&gt;&gt;</span><span class="p">:</span> <span class="n">HasPlace</span> <span class="p">{</span>
    <span class="cd">/// The type of the virtual field. This is necessarily a transparent wrapper around `P::Target`.</span>
    <span class="k">type</span> <span class="n">Wrapped</span><span class="p">:</span> <span class="n">HasPlace</span><span class="o">&lt;</span><span class="n">Target</span> <span class="o">=</span> <span class="nn">P</span><span class="p">::</span><span class="n">Target</span><span class="o">&gt;</span><span class="p">;</span>
    <span class="k">type</span> <span class="n">WrappedProj</span><span class="p">:</span> <span class="n">Projection</span><span class="o">&lt;</span><span class="n">Source</span> <span class="o">=</span> <span class="k">Self</span><span class="p">,</span> <span class="n">Target</span> <span class="o">=</span> <span class="k">Self</span><span class="p">::</span><span class="n">Wrapped</span><span class="o">&gt;</span><span class="p">;</span>
    <span class="k">fn</span> <span class="nf">wrap_proj</span><span class="p">(</span><span class="n">p</span><span class="p">:</span> <span class="o">&amp;</span><span class="n">P</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="k">Self</span><span class="p">::</span><span class="n">WrappedProj</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>This is implemented for “non-indirected containers” such as <code class="language-plaintext highlighter-rouge">MaybeUninit</code>, <code class="language-plaintext highlighter-rouge">RefCell</code>, <code class="language-plaintext highlighter-rouge">Cell</code>, etc.
What it does is that if <code class="language-plaintext highlighter-rouge">Struct</code> has a field <code class="language-plaintext highlighter-rouge">field: Field</code>, then <code class="language-plaintext highlighter-rouge">MaybeUninit&lt;Struct&gt;</code> has
a virtual field <code class="language-plaintext highlighter-rouge">field: MaybeUninit&lt;Field&gt;</code>.</p>

<p>In the next section I explain how that interacts with the existing place operations, and at the end
we’ll see examples of how they work together for very nice expressivity.</p>

<p>To explain the computations I propose a strawman syntax <code class="language-plaintext highlighter-rouge">@@Type p</code> which is allowed iff <code class="language-plaintext highlighter-rouge">Type</code> is
a transparent wrapper around the type of <code class="language-plaintext highlighter-rouge">p</code>. This expression is a place expression too, it behaves
like basically a transmute of the target without doing anything else. In particular this is how
<code class="language-plaintext highlighter-rouge">PlaceWrap</code> operates: if <code class="language-plaintext highlighter-rouge">x: MaybeUninit&lt;Struct&gt;</code>, <code class="language-plaintext highlighter-rouge">x.field</code> desugars to <code class="language-plaintext highlighter-rouge">@@MaybeUninit (*x).field</code>.</p>

<h2 id="computing-the-type-of-a-place">Computing the type of a place</h2>

<p>Every place expression starts with a local or a temporary, with a known type. We then apply one or
more of the pure place operations, recursively:</p>
<ul>
  <li>deref <code class="language-plaintext highlighter-rouge">*p</code>;</li>
  <li>field access <code class="language-plaintext highlighter-rouge">p.field</code>;</li>
  <li>indexing <code class="language-plaintext highlighter-rouge">p[i]</code>.</li>
</ul>

<p>Deref is simple: <code class="language-plaintext highlighter-rouge">*p</code> requires that <code class="language-plaintext highlighter-rouge">p: T: HasPlace</code>, and
then <code class="language-plaintext highlighter-rouge">*p: T::Target</code>.</p>

<p>Field access is the tricky one; I propose the following. Let <code class="language-plaintext highlighter-rouge">p</code> be a place expression of type <code class="language-plaintext highlighter-rouge">T</code>.</p>
<ul>
  <li>If <code class="language-plaintext highlighter-rouge">T</code> has a field <code class="language-plaintext highlighter-rouge">field: F</code>, <code class="language-plaintext highlighter-rouge">p.field: F</code> and we’re done;</li>
  <li>If <code class="language-plaintext highlighter-rouge">T: !HasPlace</code>, error.</li>
  <li>If <code class="language-plaintext highlighter-rouge">T: HasPlace</code>, we first descend through <code class="language-plaintext highlighter-rouge">T::Target::Target::etc</code> until we find a type that has
a field <code class="language-plaintext highlighter-rouge">field: F</code>. We get the intermediate expression <code class="language-plaintext highlighter-rouge">tmp_place := (****p).field: F</code> with the appropriate
number of derefs.</li>
  <li>We then “go back up” as long as the intermediate <code class="language-plaintext highlighter-rouge">T::Target::etc</code> implements <code class="language-plaintext highlighter-rouge">PlaceWrap&lt;the_right_thing&gt;</code>.
Every time we go back up in such a way, we wrap our target place in <code class="language-plaintext highlighter-rouge">tmp_place := @@Wrapped tmp_place</code>.</li>
  <li>The first time we can’t <code class="language-plaintext highlighter-rouge">PlaceWrap</code>, we’re done.</li>
  <li>If <code class="language-plaintext highlighter-rouge">T: !HasPlace</code>, error.</li>
</ul>

<p>Finally, indexing is easy because we’re only talking about built-in indexing here. It’s exactly like
a field access, where <code class="language-plaintext highlighter-rouge">[T]</code> and <code class="language-plaintext highlighter-rouge">[T; N]</code> have one field per index. The tricky part is just that  the
index is not known at compile-time. That’s the reason why <code class="language-plaintext highlighter-rouge">Projection</code>s don’t make the offset
available as a <code class="language-plaintext highlighter-rouge">const</code> actually.</p>

<p>Examples, assuming <code class="language-plaintext highlighter-rouge">Struct</code> has a field <code class="language-plaintext highlighter-rouge">field: Field</code>:</p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">p: MaybeUninit&lt;Struct&gt;</code>: <code class="language-plaintext highlighter-rouge">p.field</code> desugars to <code class="language-plaintext highlighter-rouge">@@MaybeUninit (*p).field</code> with type <code class="language-plaintext highlighter-rouge">MaybeUninit&lt;Field&gt;</code>;</li>
  <li><code class="language-plaintext highlighter-rouge">p: MaybeUninit&lt;MaybeUninit&lt;Struct&gt;&gt;</code>: <code class="language-plaintext highlighter-rouge">p.field</code> desugars to <code class="language-plaintext highlighter-rouge">@@MaybeUninit @@MaybeUninit
(**p).field</code> with type <code class="language-plaintext highlighter-rouge">MaybeUninit&lt;MaybeUninit&lt;Field&gt;&gt;</code>;</li>
  <li><code class="language-plaintext highlighter-rouge">p: &amp;&amp;&amp;MaybeUninit&lt;Struct&gt;</code>: <code class="language-plaintext highlighter-rouge">p.field</code> desugars to <code class="language-plaintext highlighter-rouge">@@MaybeUninit (****p).field</code> with type <code class="language-plaintext highlighter-rouge">MaybeUninit&lt;Foo&gt;</code>;</li>
  <li><code class="language-plaintext highlighter-rouge">p: MaybeUninit&lt;&amp;Struct&gt;</code>: <code class="language-plaintext highlighter-rouge">p.field</code> desugars to <code class="language-plaintext highlighter-rouge">(**p).field</code> with type <code class="language-plaintext highlighter-rouge">Foo</code><sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">2</a></sup>;</li>
  <li><code class="language-plaintext highlighter-rouge">p: MaybeUninit&lt;[u8]&gt;</code>: <code class="language-plaintext highlighter-rouge">p[42]</code> desugars to <code class="language-plaintext highlighter-rouge">@@MaybeUninit (*p)[42]</code> with type <code class="language-plaintext highlighter-rouge">MaybeUninit&lt;u8&gt;</code>.</li>
</ul>

<p>Note that because we resolve place expressions one operation at a time, we ensure that e.g. <code class="language-plaintext highlighter-rouge">p.a.b</code>
is always the same as <code class="language-plaintext highlighter-rouge">(p.a).b</code>.</p>

<h2 id="computing-the-type-of-borrows">Computing the type of borrows</h2>

<p>Let <code class="language-plaintext highlighter-rouge">p</code> be a place expression of type <code class="language-plaintext highlighter-rouge">T</code>. The type of <code class="language-plaintext highlighter-rouge">@Ptr p</code> is easy: it’s always
<code class="language-plaintext highlighter-rouge">Ptr&lt;Something&gt;</code>, with the guarantee that <code class="language-plaintext highlighter-rouge">Ptr&lt;Something&gt;: HasPlace&lt;Target=T&gt;</code>. This means <code class="language-plaintext highlighter-rouge">p</code>
cannot change type when this happens. There is no extra autoderef or anything at this stage.</p>

<h2 id="method-autoref">Method autoref</h2>

<p>In this section, I will assume that <code class="language-plaintext highlighter-rouge">T: Receiver</code> =&gt; <code class="language-plaintext highlighter-rouge">T: HasPlace&lt;Target=&lt;T as
Receiver&gt;::Target&gt;&gt;</code><sup id="fnref:3" role="doc-noteref"><a href="#fn:3" class="footnote" rel="footnote">3</a></sup> and that <code class="language-plaintext highlighter-rouge">T: Deref</code> =&gt; <code class="language-plaintext highlighter-rouge">T: HasPlace&lt;Target=&lt;T as Deref&gt;::Target&gt;&gt;</code>.</p>

<p>Let <code class="language-plaintext highlighter-rouge">p</code> be a place expression of type <code class="language-plaintext highlighter-rouge">T</code>, and assume we want to typecheck <code class="language-plaintext highlighter-rouge">p.method()</code>. We first
compute the set <code class="language-plaintext highlighter-rouge">{T, T::Target, T::Target::Target, ..}</code> as long as the types implement <code class="language-plaintext highlighter-rouge">HasPlace</code>.</p>

<p>For each such type <code class="language-plaintext highlighter-rouge">U</code>, we look through all the <code class="language-plaintext highlighter-rouge">impl U</code> and <code class="language-plaintext highlighter-rouge">impl Trait for U</code> for a method with
the right name. This gives us a list of “method candidates”.
If there are none, error; if there are several, pick one in some way. Which one to pick is important
for ergonomics but irrelevant for us now.</p>

<p>If the selected method takes <code class="language-plaintext highlighter-rouge">fn method(self, ..)</code> directly, we desugar to <code class="language-plaintext highlighter-rouge">&lt;..&gt;::method(***p)</code>
(with enough derefs to get to the right type) and we’re done.</p>

<p>Otherwise the method takes <code class="language-plaintext highlighter-rouge">fn method(self: X, ..)</code> where <code class="language-plaintext highlighter-rouge">X: HasPlace</code> (by the assumption
on <code class="language-plaintext highlighter-rouge">Receiver</code> above). If <code class="language-plaintext highlighter-rouge">X::Target</code> is one of the candidate types above, let <code class="language-plaintext highlighter-rouge">q := ***p</code> be <code class="language-plaintext highlighter-rouge">p</code>
suitably derefed to get to that candidate type; we then desugar to <code class="language-plaintext highlighter-rouge">&lt;..&gt;::method(@X q)</code>.
If <code class="language-plaintext highlighter-rouge">X::Target</code> is not one of the candidate types, we go back and pick another method.</p>

<p>This draft is possibly quite naive, I’ve heard that method resolution is quite tricky. Whatever
I might be missing, the core ideas I’m trying to convey are this:</p>
<ol>
  <li>We only ever consider the type of the place. The pointer the place came from does not come into
play until after we’ve desugared, to check if the borrow was allowed after all;</li>
  <li>We search only impl blocks for <code class="language-plaintext highlighter-rouge">T</code>, <code class="language-plaintext highlighter-rouge">T::Target</code>, <code class="language-plaintext highlighter-rouge">T::Target::Target</code>, etc.</li>
  <li>This works wonderfully with <a href="https://rust-lang.github.io/rfcs//3519-arbitrary-self-types-v2.html"><code class="language-plaintext highlighter-rouge">arbitrary_self_types</code></a>: when we find an arbitrary self type we can
just attempt to borrow with that pointer. This means e.g. that for <code class="language-plaintext highlighter-rouge">x: CppRef&lt;Struct&gt;</code> and <code class="language-plaintext highlighter-rouge">fn
method(self: CppRef&lt;Self&gt;)</code> on <code class="language-plaintext highlighter-rouge">Field</code>, <code class="language-plaintext highlighter-rouge">x.field.method()</code> Just Works.</li>
</ol>

<h2 id="desugaring-the-place-operations">Desugaring the place operations</h2>

<p>Recall that the operations we can do on a place are: borrow, read, write, in-place-drop<sup id="fnref:6" role="doc-noteref"><a href="#fn:6" class="footnote" rel="footnote">4</a></sup>. Each
of these comes with a corresponding <code class="language-plaintext highlighter-rouge">PlaceOp</code> trait. Once we know which operation to do on the
place, we can desugar the operation to a call to the appropriate trait method, which will also check
if that operation is allowed by the pointer in question.</p>

<p>Let’s desugar a <code class="language-plaintext highlighter-rouge">PlaceOp</code> operation on a place <code class="language-plaintext highlighter-rouge">p</code>.
A place expression is made of three things: locals, derefs and projections, where
“projections” means field accesses, indexing, and either of these mediated by <code class="language-plaintext highlighter-rouge">PlaceWrap</code>.</p>

<p>So our place <code class="language-plaintext highlighter-rouge">p</code> can always be written as <code class="language-plaintext highlighter-rouge">p = q.proj</code> where <code class="language-plaintext highlighter-rouge">.proj</code> represents all the
non-indirecting projections (including <code class="language-plaintext highlighter-rouge">PlaceWrap</code> ones), and <code class="language-plaintext highlighter-rouge">q</code> is a place expression that’s
either a local or a deref. Let <code class="language-plaintext highlighter-rouge">U</code> be the type of <code class="language-plaintext highlighter-rouge">q</code>. Then an operation on <code class="language-plaintext highlighter-rouge">p</code> desugars to
<code class="language-plaintext highlighter-rouge">PlaceOp::operate(get!(q), proj_val!(U.proj))</code>, where <code class="language-plaintext highlighter-rouge">get!</code> is defined as:</p>
<ul>
  <li>if <code class="language-plaintext highlighter-rouge">q</code> is a local, <code class="language-plaintext highlighter-rouge">get!(q)</code> is <code class="language-plaintext highlighter-rouge">&amp;raw const @@LocalPlace q</code>;</li>
  <li>otherwise <code class="language-plaintext highlighter-rouge">q</code> is a deref which we can write <code class="language-plaintext highlighter-rouge">*(r.proj2)</code>, and we can get the right pointer using
<code class="language-plaintext highlighter-rouge">PlaceDeref::deref</code><sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">5</a></sup>. This applies recursively if <code class="language-plaintext highlighter-rouge">r</code> itself contains a deref, etc.</li>
</ul>

<p>Where <code class="language-plaintext highlighter-rouge">PlaceWrap</code> comes into play is in this <code class="language-plaintext highlighter-rouge">proj_val!</code> macro: that macro computes the value of the
appropriate <code class="language-plaintext highlighter-rouge">P: Projection</code> type. If <code class="language-plaintext highlighter-rouge">PlaceWrap</code> is involved, then it will be used in computing that
projection.</p>

<h2 id="canonical-reborrows">Canonical reborrows</h2>

<p>As a special case of the borrows above, the official proposal includes a notion of <a href="https://github.com/rust-lang/rust-project-goals/issues/390#issuecomment-3644702112">“canonical
reborrows”</a>,
whereby each pointer can declare the default type with which to be reborrowed, and the (possibly
temporary) syntax <code class="language-plaintext highlighter-rouge">@$place</code> uses it.</p>

<p>The way it works is simple: <code class="language-plaintext highlighter-rouge">@$place</code> desugars just like <code class="language-plaintext highlighter-rouge">PlaceBorrow</code> above, except when we get to
<code class="language-plaintext highlighter-rouge">PlaceOp::operate</code> we use <code class="language-plaintext highlighter-rouge">&lt;PlaceBorrow&lt;'_, _, &lt;Ptr as
CanonicalReborrow&lt;proj_ty!(U.proj)&gt;&gt;::Output&gt;&gt;::borrow</code> where <code class="language-plaintext highlighter-rouge">Ptr</code> is the type of <code class="language-plaintext highlighter-rouge">*get!(q)</code>. This
is equivalent to <code class="language-plaintext highlighter-rouge">@Output $place</code> with that same <code class="language-plaintext highlighter-rouge">Output</code> type.</p>

<h2 id="putting-it-all-together">Putting it all together</h2>

<p>Let’s go through a bunch of examples. In what follows <code class="language-plaintext highlighter-rouge">e</code> is the expression of interest that we want
to desugar and typecheck. We also assume the obvious place operations on standard library types, as
well as:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="n">Struct</span> <span class="p">{</span>
    <span class="n">field</span><span class="p">:</span> <span class="n">Field</span><span class="p">,</span>
<span class="p">}</span>
<span class="k">struct</span> <span class="n">Field</span> <span class="p">{</span>
    <span class="n">value</span><span class="p">:</span> <span class="nb">u32</span><span class="p">,</span>
<span class="p">}</span>

<span class="c1">// Implements `PlaceWrap`.</span>
<span class="k">struct</span> <span class="n">W</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span> <span class="p">{</span>
  <span class="n">value</span><span class="p">:</span> <span class="n">PhantomData</span><span class="o">&lt;</span><span class="p">()</span><span class="o">&gt;</span><span class="p">,</span>
  <span class="n">wrapped</span><span class="p">:</span> <span class="n">T</span><span class="p">,</span>
<span class="p">}</span>
</code></pre></div></div>

<ol>
  <li>
    <p><code class="language-plaintext highlighter-rouge">p: &amp;mut MaybeUninit&lt;Struct&gt;</code>, <code class="language-plaintext highlighter-rouge">e := &amp;mut p.field</code></p>

    <p>We get <code class="language-plaintext highlighter-rouge">e = &amp;mut @@MaybeUninit (**p).field : &amp;mut MaybeUninit&lt;Field&gt;</code>, and the two traits involved
 are <code class="language-plaintext highlighter-rouge">PlaceWrap</code> for <code class="language-plaintext highlighter-rouge">MaybeUninit</code> and <code class="language-plaintext highlighter-rouge">PlaceBorrow&lt;P, &amp;mut P::Target&gt;</code> for <code class="language-plaintext highlighter-rouge">&amp;mut P::Source</code>. Note
 how <code class="language-plaintext highlighter-rouge">&amp;mut</code> is entirely unaware of anything special happening, and how that would work with many
 nested wrappers.</p>
  </li>
  <li>
    <p><code class="language-plaintext highlighter-rouge">x: Struct</code>, <code class="language-plaintext highlighter-rouge">impl Field { fn method(self: CppRef&lt;Self&gt;) }</code>, <code class="language-plaintext highlighter-rouge">e := x.field.method()</code></p>

    <p>We get <code class="language-plaintext highlighter-rouge">e = Field::method(@CppRef x.field)</code>. Per the section on borrows, <code class="language-plaintext highlighter-rouge">@CppRef x.field</code>
 becomes <code class="language-plaintext highlighter-rouge">@CppRef (*@@LocalPlace x).field</code>, which is allowed iff <code class="language-plaintext highlighter-rouge">LocalPlace&lt;Struct&gt;:
 PlaceBorrow&lt;P, CppRef&lt;Field&gt;&gt;</code>. The smart pointer can opt-in to that, and of course they can
 choose the nature of the resulting borrow (owning, exclusive, shared, etc).</p>
  </li>
  <li>
    <p><code class="language-plaintext highlighter-rouge">x: &amp;mut CppRef&lt;Struct&gt;</code>, <code class="language-plaintext highlighter-rouge">impl Struct { fn method(self: &amp;CppRef&lt;Self&gt;) }</code>, <code class="language-plaintext highlighter-rouge">e := x.method()</code></p>

    <p>We get <code class="language-plaintext highlighter-rouge">e = Struct::method(&amp;*x)</code>.</p>
  </li>
  <li>
    <p><code class="language-plaintext highlighter-rouge">x: &amp;mut CppRef&lt;Struct&gt;</code>, <code class="language-plaintext highlighter-rouge">impl Field { fn method(self: &amp;CppRef&lt;Self&gt;) }</code>, <code class="language-plaintext highlighter-rouge">e := x.field.method()</code></p>

    <p>I made this an error, but in theory we could desugar this to <code class="language-plaintext highlighter-rouge">Field::method(&amp;(@CppRef
 (**x).field))</code>, i.e. create a temporary <code class="language-plaintext highlighter-rouge">CppRef</code> and borrow that. We’ll pick whatever’s
 consistent with the rest of Rust I guess.</p>
  </li>
  <li>
    <p><code class="language-plaintext highlighter-rouge">x: W&lt;Struct&gt;</code>, <code class="language-plaintext highlighter-rouge">e := w.field.value</code></p>

    <p>We get <code class="language-plaintext highlighter-rouge">e = (@@W (*x).field).value : PhantomData&lt;()&gt;</code> because the real field on <code class="language-plaintext highlighter-rouge">W</code> takes
 precedence over the virtual field. If we wanted to access the <code class="language-plaintext highlighter-rouge">value</code> field of <code class="language-plaintext highlighter-rouge">Field</code>, we’d
 have to write <code class="language-plaintext highlighter-rouge">@@W (*w).field.value</code>.</p>
  </li>
  <li>
    <p><code class="language-plaintext highlighter-rouge">x: &amp;Box&lt;Arc&lt;Struct&gt;&gt;</code>, <code class="language-plaintext highlighter-rouge">impl Field { fn method(self: ArcRef&lt;Self&gt;) }</code>, <code class="language-plaintext highlighter-rouge">e := x.field.method()</code></p>

    <p>We get <code class="language-plaintext highlighter-rouge">e = Field::method(@ArcRef (***x).field)</code>. The final desugaring looks like:</p>
    <div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="k">let</span> <span class="n">tmp</span><span class="p">:</span> <span class="o">&amp;</span><span class="n">raw</span> <span class="k">const</span> <span class="n">LocalPlace</span><span class="o">&lt;&amp;</span><span class="nb">Box</span><span class="o">&lt;</span><span class="nb">Arc</span><span class="o">&lt;</span><span class="n">Struct</span><span class="o">&gt;&gt;&gt;</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">raw</span> <span class="k">const</span> <span class="o">@@</span><span class="n">LocalPlace</span> <span class="n">x</span><span class="p">;</span>
 <span class="k">let</span> <span class="n">tmp</span><span class="p">:</span> <span class="o">&amp;</span><span class="n">raw</span> <span class="k">const</span> <span class="o">&amp;</span><span class="nb">Box</span><span class="o">&lt;</span><span class="nb">Arc</span><span class="o">&lt;</span><span class="n">Struct</span><span class="o">&gt;&gt;</span> <span class="o">=</span> <span class="o">&lt;</span><span class="n">LocalPlace</span><span class="o">&lt;</span><span class="n">_</span><span class="o">&gt;</span> <span class="k">as</span> <span class="n">PlaceDeref</span><span class="o">&lt;</span><span class="n">_</span><span class="o">&gt;&gt;</span><span class="p">::</span><span class="nf">deref</span><span class="p">(</span><span class="n">tmp</span><span class="p">,</span> <span class="nd">trivial_proj_val!</span><span class="p">(</span><span class="o">&amp;</span><span class="nb">Box</span><span class="o">&lt;</span><span class="nb">Arc</span><span class="o">&lt;</span><span class="n">Struct</span><span class="o">&gt;&gt;</span><span class="p">));</span>
 <span class="k">let</span> <span class="n">tmp</span><span class="p">:</span> <span class="o">&amp;</span><span class="n">raw</span> <span class="k">const</span> <span class="nb">Box</span><span class="o">&lt;</span><span class="nb">Arc</span><span class="o">&lt;</span><span class="n">Struct</span><span class="o">&gt;&gt;</span> <span class="o">=</span> <span class="o">&lt;&amp;</span><span class="n">_</span> <span class="k">as</span> <span class="n">PlaceDeref</span><span class="o">&lt;</span><span class="n">_</span><span class="o">&gt;&gt;</span><span class="p">::</span><span class="nf">deref</span><span class="p">(</span><span class="n">tmp</span><span class="p">,</span> <span class="nd">trivial_proj_val!</span><span class="p">(</span><span class="nb">Box</span><span class="o">&lt;</span><span class="nb">Arc</span><span class="o">&lt;</span><span class="n">Struct</span><span class="o">&gt;&gt;</span><span class="p">));</span>
 <span class="k">let</span> <span class="n">tmp</span><span class="p">:</span> <span class="o">&amp;</span><span class="n">raw</span> <span class="k">const</span> <span class="nb">Arc</span><span class="o">&lt;</span><span class="n">Struct</span><span class="o">&gt;</span> <span class="o">=</span> <span class="o">&lt;</span><span class="nb">Box</span><span class="o">&lt;</span><span class="n">_</span><span class="o">&gt;</span> <span class="k">as</span> <span class="n">PlaceDeref</span><span class="o">&lt;</span><span class="n">_</span><span class="o">&gt;&gt;</span><span class="p">::</span><span class="nf">deref</span><span class="p">(</span><span class="n">tmp</span><span class="p">,</span> <span class="nd">trivial_proj_val!</span><span class="p">(</span><span class="nb">Arc</span><span class="o">&lt;</span><span class="n">Struct</span><span class="p">));</span>
 <span class="k">let</span> <span class="n">arc_ref</span><span class="p">:</span> <span class="n">ArcRef</span><span class="o">&lt;</span><span class="n">Field</span><span class="o">&gt;</span> <span class="o">=</span> <span class="o">&lt;</span><span class="n">PlaceBorrow</span><span class="o">&lt;</span><span class="nv">'_</span><span class="p">,</span> <span class="n">_</span><span class="p">,</span> <span class="n">ArcRef</span><span class="o">&lt;</span><span class="n">_</span><span class="o">&gt;&gt;&gt;</span><span class="p">::</span><span class="nf">borrow</span><span class="p">(</span><span class="n">tmp</span><span class="p">,</span> <span class="nd">proj_val!</span><span class="p">(</span><span class="n">Struct</span><span class="py">.field</span><span class="p">));</span>
 <span class="nn">Field</span><span class="p">::</span><span class="nf">method</span><span class="p">(</span><span class="n">arc_ref</span><span class="p">)</span>
</code></pre></div>    </div>

    <p>Note how only the last deref (the one of <code class="language-plaintext highlighter-rouge">Arc</code>) is involved in the reborrow. The rest are just
 <code class="language-plaintext highlighter-rouge">PlaceDeref</code>ed through.</p>
  </li>
  <li>
    <p><code class="language-plaintext highlighter-rouge">x: Arc&lt;Box&lt;Struct&gt;&gt;</code>, <code class="language-plaintext highlighter-rouge">e := @ArcRef x.field</code></p>

    <p>That’s an error. We get <code class="language-plaintext highlighter-rouge">e = @ArcRef (**x).field</code>, which uses <code class="language-plaintext highlighter-rouge">Arc as PlaceDeref</code> then <code class="language-plaintext highlighter-rouge">Box as
 PlaceBorrow&lt;'_, _, ArcRef&lt;_&gt;&gt;</code> which doesn’t exist. This is unfortunate because in principle we
 can make this <code class="language-plaintext highlighter-rouge">ArcRef&lt;Field&gt;</code>. But this would need something like <code class="language-plaintext highlighter-rouge">Arc&lt;Box&lt;Struct&gt;&gt; as
 PlaceBorrow&lt;'_, P, ArcRef&lt;Field&gt;&gt;</code> where <code class="language-plaintext highlighter-rouge">P</code> includes a deref. Projections are just an offset in
 our model currently, so that’s not allowed<sup id="fnref:5:1" role="doc-noteref"><a href="#fn:5" class="footnote" rel="footnote">1</a></sup>.</p>
  </li>
  <li>
    <p><code class="language-plaintext highlighter-rouge">x: &amp;Arc&lt;[Struct]&gt;</code>, <code class="language-plaintext highlighter-rouge">e := @x[42].field</code></p>

    <p>This desugars to <code class="language-plaintext highlighter-rouge">@ArcRef x[42].field</code>. The final desugaring looks like:</p>
    <div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="k">let</span> <span class="n">tmp</span><span class="p">:</span> <span class="o">&amp;</span><span class="n">raw</span> <span class="k">const</span> <span class="n">LocalPlace</span><span class="o">&lt;&amp;</span><span class="nb">Arc</span><span class="o">&lt;</span><span class="p">[</span><span class="n">Struct</span><span class="p">]</span><span class="o">&gt;&gt;</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">raw</span> <span class="k">const</span> <span class="o">@@</span><span class="n">LocalPlace</span> <span class="n">x</span><span class="p">;</span>
 <span class="k">let</span> <span class="n">tmp</span><span class="p">:</span> <span class="o">&amp;</span><span class="n">raw</span> <span class="k">const</span> <span class="o">&amp;</span><span class="nb">Arc</span><span class="o">&lt;</span><span class="p">[</span><span class="n">Struct</span><span class="p">]</span><span class="o">&gt;</span> <span class="o">=</span> <span class="o">&lt;</span><span class="n">LocalPlace</span><span class="o">&lt;</span><span class="n">_</span><span class="o">&gt;</span> <span class="k">as</span> <span class="n">PlaceDeref</span><span class="o">&lt;</span><span class="n">_</span><span class="o">&gt;&gt;</span><span class="p">::</span><span class="nf">deref</span><span class="p">(</span><span class="n">tmp</span><span class="p">,</span> <span class="nd">trivial_proj_val!</span><span class="p">(</span><span class="o">&amp;</span><span class="nb">Arc</span><span class="o">&lt;</span><span class="p">[</span><span class="n">Struct</span><span class="p">]</span><span class="o">&gt;</span><span class="p">));</span>
 <span class="k">let</span> <span class="n">tmp</span><span class="p">:</span> <span class="o">&amp;</span><span class="n">raw</span> <span class="k">const</span> <span class="nb">Arc</span><span class="o">&lt;</span><span class="p">[</span><span class="n">Struct</span><span class="p">]</span><span class="o">&gt;</span> <span class="o">=</span> <span class="o">&lt;&amp;</span><span class="n">_</span> <span class="k">as</span> <span class="n">PlaceDeref</span><span class="o">&lt;</span><span class="n">_</span><span class="o">&gt;&gt;</span><span class="p">::</span><span class="nf">deref</span><span class="p">(</span><span class="n">tmp</span><span class="p">,</span> <span class="nd">trivial_proj_val!</span><span class="p">(</span><span class="nb">Arc</span><span class="o">&lt;</span><span class="p">[</span><span class="n">Struct</span><span class="p">]));</span>
 <span class="k">let</span> <span class="n">arc_ref</span><span class="p">:</span> <span class="n">ArcRef</span><span class="o">&lt;</span><span class="n">Field</span><span class="o">&gt;</span> <span class="o">=</span> <span class="o">&lt;</span><span class="n">PlaceBorrow</span><span class="o">&lt;</span><span class="nv">'_</span><span class="p">,</span> <span class="n">_</span><span class="p">,</span> <span class="n">ArcRef</span><span class="o">&lt;</span><span class="n">_</span><span class="o">&gt;&gt;&gt;</span><span class="p">::</span><span class="nf">borrow</span><span class="p">(</span><span class="n">tmp</span><span class="p">,</span> <span class="nd">proj_val!</span><span class="p">([</span><span class="n">Struct</span><span class="p">][</span><span class="mi">42</span><span class="p">]</span><span class="py">.field</span><span class="p">));</span>
 <span class="n">arc_ref</span>
</code></pre></div>    </div>

    <p>Note again how the last derefed pointer is the one used to determine the reborrow.</p>
  </li>
</ol>

<p>Below are the footnotes, this theme does not distinguish them very clearly:</p>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:5" role="doc-endnote">
      <p>Also this would make inference more complicated because we’d have to try <code class="language-plaintext highlighter-rouge">PlaceBorrow</code> for each of the pointers involved, instead of having a deterministic choice like we do today. <a href="#fnref:5" class="reversefootnote" role="doc-backlink">&#8617;</a> <a href="#fnref:5:1" class="reversefootnote" role="doc-backlink">&#8617;<sup>2</sup></a></p>
    </li>
    <li id="fn:1" role="doc-endnote">
      <p>This place looks like it should be illegal but there may be wrappers for which it is usable. For <code class="language-plaintext highlighter-rouge">MaybeUninit</code> this will just be unusable because <code class="language-plaintext highlighter-rouge">MaybeUninit</code> does not implement <code class="language-plaintext highlighter-rouge">PlaceDeref</code>. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:3" role="doc-endnote">
      <p>I’m talking about the <code class="language-plaintext highlighter-rouge">Receiver</code> trait from the <a href="https://rust-lang.github.io/rfcs//3519-arbitrary-self-types-v2.html"><code class="language-plaintext highlighter-rouge">arbitrary_self_types</code></a> feature. <a href="#fnref:3" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:6" role="doc-endnote">
      <p>I’m not counting deref because deref constructs a new place on which we’ll do operations, so we’ll always start the desugaring from a non-deref operation. <a href="#fnref:6" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:2" role="doc-endnote">
      <p>I mentioned the idea of <code class="language-plaintext highlighter-rouge">PlaceDeref</code> briefly in my <a href="https://nadrieril.github.io/blog/2025/11/11/truly-first-class-custom-smart-pointers.html">original post</a> but hadn’t fleshed it out. It’s just a <code class="language-plaintext highlighter-rouge">&amp;raw const</code>-reborrow meant to only be used for nested derefs. See its proper definition <a href="https://github.com/Nadrieril/place-projections-demo/blob/a5a73414dec04370b15733b6eef6cb4215ddff6d/src/place_ops.rs#L66-L75">here</a>. <a href="#fnref:2" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Nadri</name></author><summary type="html"><![CDATA[This blog post is part of the discussions around the Field Projections project goal. Thanks to Benno Lossin and everyone involved for the very fruitful discussions!]]></summary></entry><entry><title type="html">Postfix Macros and `let place`</title><link href="https://nadrieril.github.io/blog/2025/12/09/postfix-macros-and-let-place.html" rel="alternate" type="text/html" title="Postfix Macros and `let place`" /><published>2025-12-09T21:46:00+00:00</published><updated>2025-12-09T21:46:00+00:00</updated><id>https://nadrieril.github.io/blog/2025/12/09/postfix-macros-and-let-place</id><content type="html" xml:base="https://nadrieril.github.io/blog/2025/12/09/postfix-macros-and-let-place.html"><![CDATA[<p><a href="https://github.com/rust-lang/rfcs/pull/2442">Postfix macros</a> is the feature proposal that would
allow <code class="language-plaintext highlighter-rouge">something.macro!(x, y, z)</code>. It’s been stalled for a long time on some design issues; in this
blog post I’m exploring an idea that could answer these issues.</p>

<p>The obvious way to make the feature work is to say that in <code class="language-plaintext highlighter-rouge">&lt;expr&gt;.macro!()</code>, the macro gets the
tokens for <code class="language-plaintext highlighter-rouge">&lt;expr&gt;</code> and does what it wants with them.</p>

<p>This however allows macros to break the so-called “no-backtracking rule” (coined by Tyler Mandry
IIRC): in <code class="language-plaintext highlighter-rouge">x.is_some().while! { ... }</code>, reading the <code class="language-plaintext highlighter-rouge">while</code> makes us realize that the <code class="language-plaintext highlighter-rouge">is_some()</code>
call wasn’t just a boolean value, it was an expression to be evaluated every loop. So we sort of
have to go back and re-read the beginning of the line. For purposes of reducing surprise and code
legibility, we’d like to avoid that.</p>

<p>Hence the question that the feature stalled on: can we design postfix macros that always respect the
no-backtracking rule? We would need to somehow evaluate <code class="language-plaintext highlighter-rouge">&lt;expr&gt;</code> once and pass the result to the
macro instead of passing <code class="language-plaintext highlighter-rouge">&lt;expr&gt;</code> itself. Apart from that I’ll assume that we want maximal
expressiveness.</p>

<p>This post is centrally about places and the implicit operations that surround them; check out <a href="https://nadrieril.github.io/blog/2025/12/06/on-places-and-their-magic.html">my
recent blog post on the
topic</a> for an overview
of that vocabulary.</p>

<h2 id="partial-place-evaluation">Partial Place Evaluation</h2>

<p>To get the obvious out of the way: we can’t just desugar <code class="language-plaintext highlighter-rouge">&lt;expr&gt;.method()</code> to <code class="language-plaintext highlighter-rouge">let x = &lt;expr&gt;;
x.method()</code>; that may give entirely the wrong behavior, e.g.:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="n">Foo</span> <span class="p">{</span> <span class="n">count</span><span class="p">:</span> <span class="nb">Option</span><span class="o">&lt;</span><span class="nb">u32</span><span class="o">&gt;</span> <span class="p">}</span>
<span class="k">impl</span> <span class="n">Foo</span> <span class="p">{</span>
    <span class="k">fn</span> <span class="nf">take_count</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span> <span class="k">self</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nb">Option</span><span class="o">&lt;</span><span class="nb">u32</span><span class="o">&gt;</span> <span class="p">{</span>
        <span class="c1">// That's fine</span>
        <span class="k">self</span><span class="py">.count</span><span class="nf">.take</span><span class="p">()</span>
        <span class="c1">// That creates a copy</span>
        <span class="c1">// let tmp = self.count;</span>
        <span class="c1">// tmp.take() // modifies the copy instead of the original</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>In technical terms, that’s because the LHS of a method call is a place expression. Storing
<code class="language-plaintext highlighter-rouge">&lt;expr&gt;</code> in a temporary adds an incorrect place-to-value coercion. The same applies to postfix
macros.</p>

<p>I think that the behavior we ideally want is to pre-evaluate all temporaries (that arise from
value-to-place coercion), and pass whatever remains of the expression as-is to the macro. I’ll call
that “partial place evaluation”.</p>

<p>Some examples:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="n">x</span><span class="p">:</span> <span class="n">Foo</span> <span class="o">=</span> <span class="o">...</span><span class="p">;</span>
<span class="n">x</span><span class="py">.field.macro</span><span class="o">!</span><span class="p">()</span>
<span class="c1">// becomes (there are no temporaries)</span>
<span class="k">macro</span><span class="o">!</span><span class="p">(</span><span class="n">x</span><span class="py">.field</span><span class="p">)</span>

<span class="k">impl</span> <span class="o">..</span> <span class="p">{</span> <span class="k">fn</span> <span class="nf">method</span><span class="p">(</span><span class="o">&amp;</span><span class="k">self</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="n">Foo</span> <span class="p">{</span> <span class="o">..</span> <span class="p">}</span> <span class="p">}</span>
<span class="n">x</span><span class="nf">.method</span><span class="p">()</span><span class="py">.field.macro</span><span class="o">!</span><span class="p">()</span>
<span class="c1">// becomes</span>
<span class="k">let</span> <span class="k">mut</span> <span class="n">tmp</span> <span class="o">=</span> <span class="n">x</span><span class="nf">.method</span><span class="p">();</span>
<span class="k">macro</span><span class="o">!</span><span class="p">(</span><span class="n">tmp</span><span class="py">.field</span><span class="p">)</span>
</code></pre></div></div>

<!-- I think this works, because of two things: -->
<!-- 1. Evaluating the temporaries early and storing them on the side is what compilation would do -->
<!--    anyway, so we don't lose any generality (as long as we're careful about the lifetime of -->
<!--    temporaries); -->
<!-- 2. What remains of the expression is made purely of place operations like derefs and field accesses, -->
<!--    which have no side-effect, so we don't care what the macro does with it. -->

<p>Looks easy enough, except for autoderef<sup id="fnref:6" role="doc-noteref"><a href="#fn:6" class="footnote" rel="footnote">1</a></sup>.</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="n">x</span><span class="p">:</span> <span class="nb">Box</span><span class="o">&lt;</span><span class="n">Foo</span><span class="o">&gt;</span> <span class="o">=</span> <span class="o">...</span><span class="p">;</span>
<span class="n">x</span><span class="py">.field.macro</span><span class="o">!</span><span class="p">()</span>
</code></pre></div></div>

<p>Depending on the contents of <code class="language-plaintext highlighter-rouge">macro!()</code>, this may need to expand to a call to <code class="language-plaintext highlighter-rouge">deref</code> or <code class="language-plaintext highlighter-rouge">deref_mut</code>:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="n">tmp</span> <span class="o">=</span> <span class="nn">Box</span><span class="p">::</span><span class="nf">deref</span><span class="p">(</span><span class="o">&amp;</span><span class="n">x</span><span class="p">);</span>
<span class="k">macro</span><span class="o">!</span><span class="p">((</span><span class="o">*</span><span class="n">tmp</span><span class="p">)</span><span class="py">.field</span><span class="p">)</span>
<span class="c1">// or</span>
<span class="k">let</span> <span class="n">tmp</span> <span class="o">=</span> <span class="nn">Box</span><span class="p">::</span><span class="nf">deref_mut</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span> <span class="n">x</span><span class="p">);</span>
<span class="k">macro</span><span class="o">!</span><span class="p">((</span><span class="o">*</span><span class="n">tmp</span><span class="p">)</span><span class="py">.field</span><span class="p">)</span>
</code></pre></div></div>

<p>At this point it’s hopefully clear that no simple syntactic transformation will give us what we want.</p>

<h2 id="place-aliases-aka-let-place">Place aliases, aka <code class="language-plaintext highlighter-rouge">let place</code></h2>

<p>What we’re trying to express is “compute a place once and use it many times”.
<code class="language-plaintext highlighter-rouge">let place</code> is an idea I’ve seen floating around<sup id="fnref:7" role="doc-noteref"><a href="#fn:7" class="footnote" rel="footnote">2</a></sup> which expresses exactly that:
<code class="language-plaintext highlighter-rouge">let place p = &lt;expr&gt;;</code> causes <code class="language-plaintext highlighter-rouge">&lt;expr&gt;</code> to be evaluated as a place,
and then <code class="language-plaintext highlighter-rouge">p</code> to become an alias for the place in question.
In particular, this does <em>not</em> cause a place-to-value coercion.<sup id="fnref:4" role="doc-noteref"><a href="#fn:4" class="footnote" rel="footnote">3</a></sup></p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="n">place</span> <span class="n">p</span> <span class="o">=</span> <span class="n">x</span><span class="py">.field</span><span class="p">;</span> <span class="c1">// no place-to-value, so this does not try to move out of the place</span>
<span class="nf">something</span><span class="p">(</span><span class="o">&amp;</span><span class="n">p</span><span class="p">);</span>
<span class="nf">something_else</span><span class="p">(</span><span class="n">p</span><span class="p">);</span> <span class="c1">// now this moves out</span>
<span class="c1">// would be identical to:</span>
<span class="nf">something</span><span class="p">(</span><span class="o">&amp;</span><span class="n">x</span><span class="py">.field</span><span class="p">);</span>
<span class="nf">something_else</span><span class="p">(</span><span class="n">x</span><span class="py">.field</span><span class="p">);</span> <span class="c1">// now this moves out</span>

<span class="k">let</span> <span class="n">place</span> <span class="n">p</span> <span class="o">=</span> <span class="n">x</span><span class="nf">.method</span><span class="p">()</span><span class="py">.field</span><span class="p">;</span>
<span class="nf">something</span><span class="p">(</span><span class="o">&amp;</span><span class="n">p</span><span class="p">);</span>
<span class="c1">// would be identical to:</span>
<span class="k">let</span> <span class="n">tmp</span> <span class="o">=</span> <span class="n">x</span><span class="nf">.method</span><span class="p">();</span>
<span class="nf">something</span><span class="p">(</span><span class="o">&amp;</span><span class="n">tmp</span><span class="py">.field</span><span class="p">);</span>
</code></pre></div></div>

<p>This is exactly what we need for postfix macros: <code class="language-plaintext highlighter-rouge">&lt;expr&gt;.macro!()</code> would become (using a match to
make the temporary lifetimes work as they should 🤞):</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">match</span> <span class="o">&lt;</span><span class="n">expr</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="n">place</span> <span class="n">p</span> <span class="k">=&gt;</span> <span class="k">macro</span><span class="o">!</span><span class="p">(</span><span class="n">p</span><span class="p">),</span>
<span class="p">}</span>
</code></pre></div></div>

<p>This would have the effect I propose above: any side-effects are evaluated early, and then we can do
what we want with the resulting place.</p>

<p>One of my litmus tests of expressivity for postfix macros is this <code class="language-plaintext highlighter-rouge">write!</code> macro, which ends up
working pretty straighforwardly:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">macro_rules!</span> <span class="n">write</span> <span class="p">{</span>
    <span class="p">(</span><span class="nv">$self:self</span><span class="p">,</span> <span class="nv">$val:expr</span><span class="p">)</span> <span class="k">=&gt;</span> <span class="p">({</span>
        <span class="nv">$self</span> <span class="o">=</span> <span class="nv">$val</span><span class="p">;</span> <span class="c1">// assign to the place</span>
        <span class="o">&amp;</span><span class="k">mut</span> <span class="nv">$self</span> <span class="c1">// borrow it mutably</span>
    <span class="p">})</span>
<span class="p">}</span>
<span class="k">let</span> <span class="k">mut</span> <span class="n">x</span><span class="p">;</span> <span class="c1">// borrowck understands that `write!` initializes the place!</span>
<span class="k">let</span> <span class="n">_</span> <span class="o">=</span> <span class="n">x</span><span class="py">.write</span><span class="o">!</span><span class="p">(</span><span class="nf">Some</span><span class="p">(</span><span class="mi">42</span><span class="p">))</span><span class="nf">.take</span><span class="p">();</span>
<span class="c1">// desugars to:</span>
<span class="k">let</span> <span class="n">_</span> <span class="o">=</span> <span class="k">match</span> <span class="n">x</span> <span class="p">{</span>
    <span class="n">place</span> <span class="n">p</span> <span class="k">=&gt;</span> <span class="nd">write!</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="nf">Some</span><span class="p">(</span><span class="mi">42</span><span class="p">))</span><span class="nf">.take</span><span class="p">(),</span>
<span class="p">};</span>
<span class="c1">// desugars to:</span>
<span class="k">let</span> <span class="n">_</span> <span class="o">=</span> <span class="nd">write!</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="nf">Some</span><span class="p">(</span><span class="mi">42</span><span class="p">))</span><span class="nf">.take</span><span class="p">();</span>
<span class="c1">// desugars to:</span>
<span class="k">let</span> <span class="n">_</span> <span class="o">=</span> <span class="p">{</span>
    <span class="n">x</span> <span class="o">=</span> <span class="nf">Some</span><span class="p">(</span><span class="mi">42</span><span class="p">);</span>
    <span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span> <span class="n">x</span><span class="p">)</span><span class="nf">.take</span><span class="p">()</span>
<span class="p">};</span>
</code></pre></div></div>

<h2 id="let-place-and-custom-autoderef"><code class="language-plaintext highlighter-rouge">let place</code> and custom autoderef</h2>

<p>The hard question is still autoderef<sup id="fnref:6:1" role="doc-noteref"><a href="#fn:6" class="footnote" rel="footnote">1</a></sup> :</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="k">mut</span> <span class="n">x</span><span class="p">:</span> <span class="nb">Box</span><span class="o">&lt;</span><span class="n">Foo</span><span class="o">&gt;</span> <span class="o">=</span> <span class="o">...</span><span class="p">;</span>
<span class="k">let</span> <span class="n">place</span> <span class="n">p</span> <span class="o">=</span> <span class="n">x</span><span class="py">.field</span><span class="p">;</span> <span class="c1">// should this use `deref` or `deref_mut`?</span>
<span class="nf">something</span><span class="p">(</span><span class="o">&amp;</span><span class="n">p</span><span class="p">);</span>
<span class="nf">something_else</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span> <span class="n">p</span><span class="p">);</span> <span class="c1">// causes `deref_mut` to be called above</span>
</code></pre></div></div>

<p>For that to work, we infer for each place alias whether it is used by-ref, by-ref-mut or by-move
(like closure captures I think), and propagate this information to its declaration so that we can
know which <code class="language-plaintext highlighter-rouge">Deref</code> variant to call <sup id="fnref:5" role="doc-noteref"><a href="#fn:5" class="footnote" rel="footnote">4</a></sup>.</p>

<h2 id="let-place-isnt-too-powerful"><code class="language-plaintext highlighter-rouge">let place</code> isn’t too powerful</h2>

<p>Turns out <code class="language-plaintext highlighter-rouge">let place</code> is a rather simple feature when we play with it:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Place aliases can't be reassigned:</span>
<span class="k">let</span> <span class="n">place</span> <span class="n">p</span> <span class="o">=</span> <span class="n">x</span><span class="py">.field</span><span class="p">;</span>
<span class="c1">// Warning, this assigns to `x.field` here! that's what we want place aliases to do</span>
<span class="c1">// but it's admittedly surprising.</span>
<span class="n">p</span> <span class="o">=</span> <span class="n">x</span><span class="py">.other_field</span><span class="p">;</span>

<span class="c1">// You can't end the scope of a place alias by hand:</span>
<span class="k">let</span> <span class="n">place</span> <span class="n">p</span> <span class="o">=</span> <span class="n">x</span><span class="py">.field</span><span class="p">;</span>
<span class="nf">drop</span><span class="p">(</span><span class="n">p</span><span class="p">);</span> <span class="c1">// oops you moved out of `x.field`</span>
<span class="c1">// `p` is still usable here, e.g. you can assign to it</span>

<span class="c1">// Place aliases can't be conditional.</span>
<span class="k">let</span> <span class="n">place</span> <span class="n">p</span> <span class="o">=</span> <span class="k">if</span> <span class="nf">foo</span><span class="p">()</span> <span class="p">{</span> <span class="c1">// value-to-place happens at the assignment</span>
    <span class="n">x</span><span class="py">.field</span> <span class="c1">// place-to-value happens here</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
    <span class="n">x</span><span class="py">.other_field</span>
<span class="p">};</span>
<span class="c1">// This won't mutate either of the fields, `p` is fresh from a value-to-place coercion. I propose</span>
<span class="c1">// that this should just be an error to avoid sadness.</span>
<span class="nf">do_something</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span> <span class="n">p</span><span class="p">);</span>
</code></pre></div></div>

<p>In particular it’s easy to statically know what each place alias is an alias for.</p>

<p>The caveat is that all of those are surprising if you think of <code class="language-plaintext highlighter-rouge">p</code> as a variable. This is definitely
not a beginners feature.</p>

<h2 id="let-place-doesnt-need-to-exist-in-mir"><code class="language-plaintext highlighter-rouge">let place</code> doesn’t need to exist in MIR</h2>

<p>The big question that <code class="language-plaintext highlighter-rouge">let place</code> raises is what this even means in the operational semantics of
Rust. Do we need a new notion of “place alias” in <a href="https://github.com/minirust/minirust">MiniRust</a>?</p>

<p>I think not. The reason is that the “store intermediate values in temporaries” happens automatically
when we lower to MIR. All place coercions and such are explicit, and MIR place expressions do not cause
side-effects. So whenever we lower a <code class="language-plaintext highlighter-rouge">let place p</code> to MIR, we can record what <code class="language-plaintext highlighter-rouge">mir::Place</code> <code class="language-plaintext highlighter-rouge">p</code>
stands for and substitute it wherever it’s used.</p>

<p>To ensure that the original place doesn’t get used while the alias is live, we insert a fake borrow
where the <code class="language-plaintext highlighter-rouge">let place</code> is taken and fake reads when it’s referenced. That’s already a trick we use
in MIR lowering for exactly this purpose<sup id="fnref:3" role="doc-noteref"><a href="#fn:3" class="footnote" rel="footnote">5</a></sup>.</p>

<p>So the only difficulty seems to be the mutability inference mentioned in previous section. The rest
of typechecking <code class="language-plaintext highlighter-rouge">let place</code> is straighforward: <code class="language-plaintext highlighter-rouge">let place p = &lt;expr&gt;;</code> makes a place with the same
type as <code class="language-plaintext highlighter-rouge">&lt;expr&gt;</code>, and then it behaves pretty much like a local variable.</p>

<p>All in all this is looking like a much simpler feature that I expected when I started playing with
it.</p>

<h2 id="let-place-is-fun"><code class="language-plaintext highlighter-rouge">let place</code> is fun</h2>

<p>I kinda of want it just because it’s cute. It makes explicit something implicit in a rather elegant
way. Here are some fun things I discovered about it.</p>

<p>To start with, it kind of subsumes binding modes in patterns: <code class="language-plaintext highlighter-rouge">if let Some(ref x) = ...</code> is the same
thing as <code class="language-plaintext highlighter-rouge">if let Some(place p) = ... &amp;&amp; let x = &amp;p</code>. One could even use <code class="language-plaintext highlighter-rouge">place x</code> instead of <code class="language-plaintext highlighter-rouge">x</code> in
patterns everywhere and let autoref set the right binding mode! That’s a funky alternative to match
ergonomics.</p>

<p>We can also use it to explain this one weird corner case of borrow-checking. This code is rejected
by the borrow-checker, can you tell why?</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="k">mut</span> <span class="n">x</span><span class="p">:</span> <span class="o">&amp;</span><span class="p">[</span><span class="n">_</span><span class="p">]</span> <span class="o">=</span> <span class="o">&amp;</span><span class="p">[[</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">]];</span>
<span class="k">let</span> <span class="n">y</span><span class="p">:</span> <span class="o">&amp;</span><span class="p">[</span><span class="n">_</span><span class="p">]</span> <span class="o">=</span> <span class="o">&amp;</span><span class="p">[];</span>
<span class="k">let</span> <span class="n">_</span> <span class="o">=</span> <span class="n">x</span><span class="p">[</span><span class="mi">0</span><span class="p">][{</span><span class="n">x</span> <span class="o">=</span> <span class="n">y</span><span class="p">;</span> <span class="mi">1</span><span class="p">}];</span>
<span class="c1">//      ^^^^ value is immutable in indexing expression</span>
</code></pre></div></div>
<p>What’s happening is that we do the first bound-check on <code class="language-plaintext highlighter-rouge">x</code> before we evaluate the second index
expression. So we can’t have that expression invalidate the bound-check on pain of UB. We can use
<code class="language-plaintext highlighter-rouge">let place</code> to explain the situation via a desugaring:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">x</span><span class="p">[</span><span class="mi">0</span><span class="p">][{</span><span class="n">x</span> <span class="o">=</span> <span class="n">y</span><span class="p">;</span> <span class="mi">1</span><span class="p">}]</span>
<span class="c1">// desugars to:</span>
<span class="k">let</span> <span class="n">place</span> <span class="n">p</span> <span class="o">=</span> <span class="n">x</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span> <span class="c1">// bounds check happens here</span>
<span class="n">p</span><span class="p">[{</span><span class="n">x</span> <span class="o">=</span> <span class="n">y</span><span class="p">;</span> <span class="mi">1</span><span class="p">}]</span>
<span class="c1">// desugars to:</span>
<span class="k">let</span> <span class="n">place</span> <span class="n">p</span> <span class="o">=</span> <span class="n">x</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span>
<span class="k">let</span> <span class="n">index</span> <span class="o">=</span> <span class="p">{</span><span class="n">x</span> <span class="o">=</span> <span class="n">y</span><span class="p">;</span> <span class="mi">1</span><span class="p">};</span> <span class="c1">// x modified here</span>
<span class="n">p</span><span class="p">[</span><span class="n">index</span><span class="p">]</span> <span class="c1">// but place alias used again here</span>
</code></pre></div></div>

<p>Can this be used to explain closure captures? I don’t think so because closures really do carry
borrows of places, not just places. It does feel like a related kind of magic though.</p>

<h3 id="conclusion">Conclusion</h3>

<p>I started out writing this blog post not knowing where it would lead, and I’m stoked of how clean
this proposal ended up looking. I kinda want <code class="language-plaintext highlighter-rouge">let place</code> even independently from postfix macros. The
one weird thing about <code class="language-plaintext highlighter-rouge">let place</code> is this “mutability inference” for autoderef, hopefully that’s an
acceptable complication.</p>

<p>I’m most looking forward to everyone’s feedback on this; <code class="language-plaintext highlighter-rouge">let place</code> is rather fresh and I wanna
know if I missed anything important (or anything fun!).</p>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:6" role="doc-endnote">
      <p>Well <code class="language-plaintext highlighter-rouge">Box</code> doesn’t actually use <code class="language-plaintext highlighter-rouge">Deref</code>/<code class="language-plaintext highlighter-rouge">DerefMut</code> because it’s built into the borrow-checker, but that’s the easiest type to use for illustration so forgive me. <a href="#fnref:6" class="reversefootnote" role="doc-backlink">&#8617;</a> <a href="#fnref:6:1" class="reversefootnote" role="doc-backlink">&#8617;<sup>2</sup></a></p>
    </li>
    <li id="fn:7" role="doc-endnote">
      <p>I can’t find references to that idea apart from <a href="https://rust-lang.zulipchat.com/#narrow/channel/213817-t-lang/topic/let.20place/with/421534614">this thread</a>, so maybe I’m the one who came up with it. I can find <a href="https://internals.rust-lang.org/t/idea-placepattern/11090">this</a> that uses the same syntax but for a different purpose. <a href="#fnref:7" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:4" role="doc-endnote">
      <p>In a way <code class="language-plaintext highlighter-rouge">let place</code> creates a sort of magic reference to the place: you can move out of it (if allowed), mutate it (if allowed), get shared access to it (if allowed). The “magic” part is that the permissions that the magic reference requires are inferred from how the reference is used, instead of declared up front like for <code class="language-plaintext highlighter-rouge">&amp;</code>, <code class="language-plaintext highlighter-rouge">&amp;mut</code> and proposed extensions like <code class="language-plaintext highlighter-rouge">&amp;pin mut</code>, <code class="language-plaintext highlighter-rouge">&amp;own</code> and <code class="language-plaintext highlighter-rouge">&amp;uninit</code>. <a href="#fnref:4" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:5" role="doc-endnote">
      <p>You might thing this gets more complicated with <a href="https://nadrieril.github.io/blog/2025/11/11/truly-first-class-custom-smart-pointers.html">custom places and field projections</a>, but actually for those we have no choice but to call the appropriate <code class="language-plaintext highlighter-rouge">PlaceOperation</code> trait method only when we know what operation is being done on the place, so there’s no need to infer anything. The question of how to represent this in MIR may get a bit more tricky though. <a href="#fnref:5" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:3" role="doc-endnote">
      <p>See the indexing example below, which I took from the <a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/mir/enum.BorrowKind.html#variant.Fake">doc on fake borrows</a>. <a href="#fnref:3" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Nadri</name></author><summary type="html"><![CDATA[Postfix macros is the feature proposal that would allow something.macro!(x, y, z). It’s been stalled for a long time on some design issues; in this blog post I’m exploring an idea that could answer these issues.]]></summary></entry><entry><title type="html">The Implicit Magics of Places</title><link href="https://nadrieril.github.io/blog/2025/12/06/on-places-and-their-magic.html" rel="alternate" type="text/html" title="The Implicit Magics of Places" /><published>2025-12-06T19:32:00+00:00</published><updated>2025-12-06T19:32:00+00:00</updated><id>https://nadrieril.github.io/blog/2025/12/06/on-places-and-their-magic</id><content type="html" xml:base="https://nadrieril.github.io/blog/2025/12/06/on-places-and-their-magic.html"><![CDATA[<p>Josh Triplett and I are trying to revive <a href="https://github.com/rust-lang/rfcs/pull/2442">postfix
macros</a>, and this raises interesting questions about
places. Places are a part of the language where a lot of implicit things happen, so we need good
vocabulary to talk about them.</p>

<p>In this post I’ll give a brief overview of what places are, the implicit operations that surround
them, and the vocabulary we have for them. I’ll talk more about postfix macros in a later post.</p>

<p>A lot of this blog post is a retelling of <a href="https://www.ralfj.de/blog/2024/08/14/places.html">this blog post from
Ralf</a> in my own words; do check out his post if
you want a more rigorous presentation or are interested in how this is relevant for unsafe code.</p>

<h2 id="places-and-place-expressions">Places and place expressions</h2>

<p>Rust expressions typically evaluate to a value: to evaluate <code class="language-plaintext highlighter-rouge">x + 1</code> we first evaluate <code class="language-plaintext highlighter-rouge">x</code> to its
value, say <code class="language-plaintext highlighter-rouge">42</code>, then compute <code class="language-plaintext highlighter-rouge">42 + 1</code> which results in the value <code class="language-plaintext highlighter-rouge">43</code>. But we don’t evaluate <code class="language-plaintext highlighter-rouge">&amp;mut
x</code> to <code class="language-plaintext highlighter-rouge">&amp;mut 42</code>, that would make no sense. We want the result of <code class="language-plaintext highlighter-rouge">&amp;mut x</code> to be about <code class="language-plaintext highlighter-rouge">x</code> as
a memory location, not <code class="language-plaintext highlighter-rouge">x</code> as a value.</p>

<p>This is what places are: a place is a memory location, and some Rust expressions refer to places.
We saw that a local variable <code class="language-plaintext highlighter-rouge">x</code> denotes a place, whereas <code class="language-plaintext highlighter-rouge">42</code> only denotes a value.</p>

<p>Rust expressions are of two kinds: they denote either a place or a value<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup>. We call them “place
expressions” or “value expressions” depending on which. The following are all the place expressions
in Rust:</p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">&lt;ident&gt;</code>, where the ident is the name of a local variable or static;</li>
  <li>Deref <code class="language-plaintext highlighter-rouge">*&lt;expr&gt;</code>;</li>
  <li>Field access <code class="language-plaintext highlighter-rouge">&lt;expr&gt;.field</code>;</li>
  <li>Indexing <code class="language-plaintext highlighter-rouge">&lt;expr&gt;[&lt;expr&gt;]</code>.</li>
</ul>

<p>All the other expressions (e.g. method call <code class="language-plaintext highlighter-rouge">&lt;expr&gt;.method()</code>, arithmetic operation <code class="language-plaintext highlighter-rouge">&lt;expr&gt; + &lt;expr&gt;</code>,
constant <code class="language-plaintext highlighter-rouge">42</code>, etc) are value expressions.</p>

<p>On the other side of this, each operation takes either a place or a value. Each operand of an
operation is either a “place context” or “value context” depending on which. The criterion is,
roughly: if the operation cares only about the value of that expression then it’s a value context,
if it also cares about where the value is stored then it’s a place context.</p>

<p>The following operations are all place contexts:</p>
<ul>
  <li>Borrows <code class="language-plaintext highlighter-rouge">&amp;mut &lt;expr&gt;</code>, <code class="language-plaintext highlighter-rouge">&amp;&lt;expr&gt;</code>, <code class="language-plaintext highlighter-rouge">&amp;raw const &lt;expr&gt;</code>, <code class="language-plaintext highlighter-rouge">&amp;raw mut &lt;expr&gt;</code>;</li>
  <li>The LHS<sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">2</a></sup> of a write assignment <code class="language-plaintext highlighter-rouge">&lt;expr&gt; = ...;</code>;</li>
  <li>The RHS of an assignment <code class="language-plaintext highlighter-rouge">let ... = &lt;expr&gt;;</code>, <code class="language-plaintext highlighter-rouge">... = &lt;expr&gt;;</code><sup id="fnref:3" role="doc-noteref"><a href="#fn:3" class="footnote" rel="footnote">3</a></sup>;</li>
  <li>The scrutinee of a match <code class="language-plaintext highlighter-rouge">match &lt;expr&gt; { ... }</code>;</li>
  <li>The LHS of a field access <code class="language-plaintext highlighter-rouge">&lt;expr&gt;.field</code>;</li>
  <li>The LHS of an indexing operation <code class="language-plaintext highlighter-rouge">&lt;expr&gt;[...]</code>;</li>
  <li>The LHS of a method call <code class="language-plaintext highlighter-rouge">&lt;expr&gt;.method(..)</code>.</li>
</ul>

<p>The following operations are all value contexts:</p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">&lt;expr&gt; + &lt;expr&gt;</code>, <code class="language-plaintext highlighter-rouge">&lt;expr&gt; - &lt;expr&gt;</code> etc;</li>
  <li><code class="language-plaintext highlighter-rouge">*&lt;expr&gt;</code>;</li>
  <li><code class="language-plaintext highlighter-rouge">(&lt;expr&gt;, &lt;expr&gt;)</code> as well as struct and enum constructors;</li>
  <li><code class="language-plaintext highlighter-rouge">{ &lt;expr&gt; }</code>.</li>
</ul>

<p>These lists are not exhaustive.</p>

<h2 id="place-to-value-coercion">Place-to-value coercion</h2>

<p>So what happens when you put a place expression in a value context? Rust inserts an implicit read of
the value inside the place. This is called “place-to-value coercion” and following Ralf I’ll write
it “<code class="language-plaintext highlighter-rouge">load</code>”:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="n">z</span> <span class="o">=</span> <span class="n">x</span> <span class="o">+</span> <span class="n">y</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span> <span class="c1">// `x` and `y` are place expressions in a value context</span>
<span class="c1">// actually means:</span>
<span class="k">let</span> <span class="n">z</span> <span class="o">=</span> <span class="n">load</span> <span class="n">x</span> <span class="o">+</span> <span class="n">load</span> <span class="n">y</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span>

<span class="k">let</span> <span class="n">x</span> <span class="o">=</span> <span class="nf">Some</span><span class="p">(</span><span class="o">*</span><span class="n">ptr</span><span class="p">);</span> <span class="c1">// `*ptr` is a place expression in a value context</span>
<span class="c1">// actually means:</span>
<span class="k">let</span> <span class="n">x</span> <span class="o">=</span> <span class="nf">Some</span><span class="p">(</span><span class="n">load</span> <span class="o">*</span><span class="n">ptr</span><span class="p">);</span>
</code></pre></div></div>

<p>If the place expression has a non-<code class="language-plaintext highlighter-rouge">Copy</code> type, then place-to-value coercion will move the value out
(or raise an error).
E.g.:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="n">x</span> <span class="o">=</span> <span class="nn">Box</span><span class="p">::</span><span class="nf">new</span><span class="p">(</span><span class="mi">42</span><span class="p">);</span>
<span class="k">let</span> <span class="n">y</span> <span class="o">=</span> <span class="nf">Some</span><span class="p">(</span><span class="n">x</span><span class="p">);</span>
<span class="c1">// actually means:</span>
<span class="k">let</span> <span class="n">y</span> <span class="o">=</span> <span class="nf">Some</span><span class="p">(</span><span class="n">load</span> <span class="n">x</span><span class="p">);</span> <span class="c1">// this moves out of `x`</span>
</code></pre></div></div>
<p>In fact whenever you get the “cannot move out of a shared reference” error, you know there was
a place-to-value coercion somewhere.</p>

<h2 id="value-to-place-coercion">Value-to-place coercion</h2>

<p>How about the other way around, can a value expression be put in a place context? Absolutely, and we
then get “value-to-place coercion”, also called “storing the value in a temporary place”. A simple
example is:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="n">x</span> <span class="o">=</span> <span class="nf">f</span><span class="p">(</span><span class="o">&amp;</span><span class="nn">String</span><span class="p">::</span><span class="nf">from</span><span class="p">(</span><span class="sc">'🦀'</span><span class="p">));</span>
<span class="c1">// actually means something like:</span>
<span class="k">let</span> <span class="n">x</span> <span class="o">=</span> <span class="p">{</span>
    <span class="k">let</span> <span class="n">tmp</span> <span class="o">=</span> <span class="nn">String</span><span class="p">::</span><span class="nf">from</span><span class="p">(</span><span class="sc">'🦀'</span><span class="p">);</span>
    <span class="nf">f</span><span class="p">(</span><span class="o">&amp;</span><span class="n">tmp</span><span class="p">)</span>
<span class="p">};</span>
</code></pre></div></div>

<p>This actually happens quite often, with method autoref (which I’ll go into in a moment), e.g.:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">if</span> <span class="n">x</span><span class="nf">.method</span><span class="p">()</span><span class="nf">.is_some</span><span class="p">()</span> <span class="p">{</span>
    <span class="o">...</span>
<span class="p">}</span>
<span class="c1">// method resolution + autoref desugars this to:</span>
<span class="k">if</span> <span class="nn">Option</span><span class="p">::</span><span class="nf">is_some</span><span class="p">(</span><span class="o">&amp;</span><span class="n">x</span><span class="nf">.method</span><span class="p">())</span> <span class="p">{</span> <span class="c1">// `x.method()` is a value expression in a place context</span>
    <span class="o">...</span>
<span class="p">}</span>
</code></pre></div></div>

<p>This direction of coercion is much trickier than the other, because it raises the thorny question of
how long that implicit temporary place should live.
That topic is called “temporary lifetime extension rules” and you should check out <a href="https://blog.m-ou.se/super-let/">Mara’s blog post
on the topic</a><sup id="fnref:4" role="doc-noteref"><a href="#fn:4" class="footnote" rel="footnote">4</a></sup> to get a sense of the space.</p>

<h2 id="autoderef">Autoderef</h2>

<p>Autoderef is, to start with, what allows you to write things like <code class="language-plaintext highlighter-rouge">x.field</code> when <code class="language-plaintext highlighter-rouge">x: &amp;T</code>. The
compiler will desugar this to <code class="language-plaintext highlighter-rouge">(*x).field</code>. This works with any number of references: <code class="language-plaintext highlighter-rouge">x: &amp;&amp;mut &amp;T</code>
gives <code class="language-plaintext highlighter-rouge">(***x).field</code>.</p>

<p>This also happens on method calls, inside function arguments (you can pass a <code class="language-plaintext highlighter-rouge">&amp;&amp;mut &amp;T</code>
to a function expecting a <code class="language-plaintext highlighter-rouge">&amp;T</code>), and a bunch of other cases I couldn’t list exactly<sup id="fnref:7" role="doc-noteref"><a href="#fn:7" class="footnote" rel="footnote">5</a></sup>.</p>

<p>Autoderef is in fact more powerful than this: it applies not only to built-in references but to any
smart pointer that implements <code class="language-plaintext highlighter-rouge">Deref</code>. So if <code class="language-plaintext highlighter-rouge">x: Box&lt;T&gt;</code><sup id="fnref:5" role="doc-noteref"><a href="#fn:5" class="footnote" rel="footnote">6</a></sup>, <code class="language-plaintext highlighter-rouge">x.field</code> becomes
<code class="language-plaintext highlighter-rouge">(*Box::deref(&amp;x)).field</code> (note the automatic borrow of <code class="language-plaintext highlighter-rouge">x</code>). Well, unless you’re about to use
<code class="language-plaintext highlighter-rouge">x.field</code> mutably, in which case it becomes <code class="language-plaintext highlighter-rouge">(*Box::deref_mut(&amp;mut x)).field</code>.</p>

<p>And this is where autoderef is very magical: if <code class="language-plaintext highlighter-rouge">x</code> is a smart pointer then <code class="language-plaintext highlighter-rouge">*x</code> by itself is
a place, whose type is known, but that we won’t know how to compute until we know what we’re doing
to the place. A mutable borrow or assignment causes <code class="language-plaintext highlighter-rouge">deref_mut</code> to be called, otherwise <code class="language-plaintext highlighter-rouge">deref</code> is
called<sup id="fnref:6" role="doc-noteref"><a href="#fn:6" class="footnote" rel="footnote">7</a></sup>.</p>

<h2 id="autoref-and-method-resolution">Autoref and method resolution</h2>

<p>The final piece of the puzzle is what happens on method calls. This might be the most magical
desugaring we have: method resolution. Two things happen for <code class="language-plaintext highlighter-rouge">&lt;expr&gt;.method()</code>: we have to figure
out what method to call, and in the process may have to change <code class="language-plaintext highlighter-rouge">&lt;expr&gt;</code>.</p>

<p>Take a simple example:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="k">mut</span> <span class="n">x</span> <span class="o">=</span> <span class="nf">Some</span><span class="p">(</span><span class="mi">42</span><span class="p">);</span>
<span class="k">let</span> <span class="n">y</span> <span class="o">=</span> <span class="n">x</span><span class="nf">.take</span><span class="p">();</span>
</code></pre></div></div>
<p>Here <code class="language-plaintext highlighter-rouge">x</code> has type <code class="language-plaintext highlighter-rouge">Option&lt;i32&gt;</code>, so we look at all the methods on <code class="language-plaintext highlighter-rouge">Option</code> and find <code class="language-plaintext highlighter-rouge">fn take(&amp;mut
self)</code>. To make the type match, we insert a borrow of <code class="language-plaintext highlighter-rouge">x</code>. The desugared call is <code class="language-plaintext highlighter-rouge">Option::take(&amp;mut
x)</code>. This process of “adding extra references when needed” is called “autoref”.</p>

<p>Method resolution can also involve autoderef: if <code class="language-plaintext highlighter-rouge">x: Rc&lt;i32&gt;</code>, <code class="language-plaintext highlighter-rouge">x.is_some()</code> will first look for an
<code class="language-plaintext highlighter-rouge">is_some</code> method on <code class="language-plaintext highlighter-rouge">Rc</code>, then fallback to autoderef and try again. We end up with
<code class="language-plaintext highlighter-rouge">Option::is_some(Rc::deref(&amp;x))</code>. The <a href="https://doc.rust-lang.org/reference/expressions/method-call-expr.html#r-expr.method.autoref-deref">full
algorithm</a>
involves a mix of autoderef and autoref.</p>

<h2 id="closure-capture">Closure capture</h2>

<p>Closures add another layer of magic to places: if you mention inside a closure a place that comes
from outside the closure, the place will automatically get carried around along with the closure. We
say the place is “captured”, and this means either that we place-to-value coerce and store the
resulting value inside the closure, or that we store a <code class="language-plaintext highlighter-rouge">&amp;</code> or <code class="language-plaintext highlighter-rouge">&amp;mut</code> borrow of the place inside the
closure. Much like for autoderef, the way we capture <code class="language-plaintext highlighter-rouge">x</code> depends on how the place is used.</p>

<p>For example, this:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="n">x</span><span class="p">:</span> <span class="n">Foo</span> <span class="o">=</span> <span class="o">...</span><span class="p">;</span>
<span class="k">let</span> <span class="n">f</span> <span class="o">=</span> <span class="p">||</span> <span class="p">{</span>
    <span class="n">x</span><span class="py">.field</span><span class="nf">.is_some</span><span class="p">()</span>
<span class="p">};</span>
</code></pre></div></div>
<p>causes <code class="language-plaintext highlighter-rouge">x.field</code> to be captured, in this case as a shared borrow. The resulting code is equivalent
to the following, where we make the closure object explicit:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="n">Closure</span><span class="o">&lt;</span><span class="nv">'a</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="n">p</span><span class="p">:</span> <span class="o">&amp;</span><span class="nv">'a</span> <span class="n">Field</span><span class="p">,</span>
<span class="p">}</span>
<span class="k">impl</span> <span class="nb">Fn</span><span class="o">&lt;</span><span class="p">()</span><span class="o">&gt;</span> <span class="k">for</span> <span class="n">Closure</span><span class="o">&lt;</span><span class="nv">'_</span><span class="o">&gt;</span> <span class="p">{</span> <span class="c1">// I'm cheating a bit on the shape of this trait</span>
    <span class="k">type</span> <span class="n">Output</span> <span class="o">=</span> <span class="nb">bool</span><span class="p">;</span>
    <span class="k">fn</span> <span class="nf">call</span><span class="p">(</span><span class="o">&amp;</span><span class="k">self</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nb">bool</span> <span class="p">{</span>
        <span class="nn">Option</span><span class="p">::</span><span class="nf">is_some</span><span class="p">((</span><span class="o">*</span><span class="k">self</span><span class="p">)</span><span class="py">.p</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>
<span class="c1">// We store a borrow of `x.field` inside the closure.</span>
<span class="k">let</span> <span class="n">f</span> <span class="o">=</span> <span class="n">Closure</span> <span class="p">{</span> <span class="n">p</span><span class="p">:</span> <span class="o">&amp;</span><span class="n">x</span><span class="py">.field</span> <span class="p">};</span>
</code></pre></div></div>

<p>The rules for what we capture exactly are subtle, see <a href="https://doc.rust-lang.org/reference/types/closure.html#capture-modes">the
Reference</a> for details.</p>

<h2 id="conclusion">Conclusion</h2>

<p>In this whirlwind tour, we saw that places are at the center of a number of implicit operations:</p>
<ul>
  <li>place-to-value coercion;</li>
  <li>value-to-place coercion (with temporary lifetime extension);</li>
  <li>autoderef;</li>
  <li>autoref along with method resolution;</li>
  <li>closure capture.</li>
</ul>

<p>Places get implicitly borrowed, created, moved out of and discarded all of the time implicitly. This
all comes together to “just work” most of the time, and I’d say play a big role in Rust’s renowned
expressivity power, but is far from obvious when you start digging.</p>

<p>I hope this post gave you a clearer picture, and I for one know I’ll be referencing this blog post
in the future. I expect to keep it up-to-date/add more detail to it, more like a reference document
than a one-off blog post.</p>
<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1" role="doc-endnote">
      <p>For extra detail, you may enjoy <a href="https://doc.rust-lang.org/reference/expressions.html#place-expressions-and-value-expressions">the corresponding section of the Reference</a> <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:2" role="doc-endnote">
      <p>“LHS” stands for “left-hand-side” and “RHS” for “right-hand-side”. <a href="#fnref:2" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:3" role="doc-endnote">
      <p>You might think this is a value context because <code class="language-plaintext highlighter-rouge">let x = &lt;expr&gt;;</code> does cause place-to-value coercion. The trick is “patterns”: <code class="language-plaintext highlighter-rouge">let ref mut x = &lt;expr&gt;;</code> does the same as <code class="language-plaintext highlighter-rouge">let x = &amp;mut &lt;expr&gt;</code>, which is very much a place context. And you can mix it up: <code class="language-plaintext highlighter-rouge">let (ref mut x, y, _) = &lt;expr&gt;;</code> does one value-to-place coercion for <code class="language-plaintext highlighter-rouge">y</code> and considers the rest of <code class="language-plaintext highlighter-rouge">&lt;expr&gt;</code> as a place. <a href="#fnref:3" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:4" role="doc-endnote">
      <p>As it says there, that blog post was part of the discussion around temporary lifetime changes for Rust’s 2024 edition. Edition 2024 is now the default one, so part of the rules presented in that post have now changed. <a href="https://github.com/rust-lang/rfcs/pull/3606">Here</a> is for example on such change. <a href="#fnref:4" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:7" role="doc-endnote">
      <p>I think the rule is that it happens at <a href="https://doc.rust-lang.org/reference/type-coercions.html?#r-coerce.site">coercion sites</a>. <a href="#fnref:7" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:5" role="doc-endnote">
      <p>Well <code class="language-plaintext highlighter-rouge">Box</code> doesn’t actually use <code class="language-plaintext highlighter-rouge">Deref</code>/<code class="language-plaintext highlighter-rouge">DerefMut</code> because it’s built into the borrow-checker, but that’s the easiest type to use for illustration so forgive me. <a href="#fnref:5" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:6" role="doc-endnote">
      <p>And well <code class="language-plaintext highlighter-rouge">Box</code> also supports moving out of fields, which is deeply magical and which we’ll ignore here. <a href="#fnref:6" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Nadri</name></author><summary type="html"><![CDATA[Josh Triplett and I are trying to revive postfix macros, and this raises interesting questions about places. Places are a part of the language where a lot of implicit things happen, so we need good vocabulary to talk about them.]]></summary></entry><entry><title type="html">Pinning is a kind of static borrow</title><link href="https://nadrieril.github.io/blog/2025/11/12/pinning-is-a-kind-of-static-borrow.html" rel="alternate" type="text/html" title="Pinning is a kind of static borrow" /><published>2025-11-12T23:20:00+00:00</published><updated>2025-11-12T23:20:00+00:00</updated><id>https://nadrieril.github.io/blog/2025/11/12/pinning-is-a-kind-of-static-borrow</id><content type="html" xml:base="https://nadrieril.github.io/blog/2025/11/12/pinning-is-a-kind-of-static-borrow.html"><![CDATA[<blockquote>
  <p>Disclaimer: I am by no means a <code class="language-plaintext highlighter-rouge">Pin</code> expert so this may be incorrect in subtle ways. Please let me
know if so!</p>
</blockquote>

<p>Pin is notoriously subtle: once pinned, a place has restricted access <em>forever</em>, even after the
<code class="language-plaintext highlighter-rouge">Pin&lt;&amp;mut T&gt;</code> reference goes out of scope. The reason is simple: we want to allow pointers to the
place to be stored in locations we know nothing about, so the data there better stay consistent.</p>

<p>In other words, a place has restricted access because some pointers to it may exist. Well this is
typically the kind of thing the borrow-checker tracks!</p>

<p>I propose the following idea, for the sole purpose of making <code class="language-plaintext highlighter-rouge">Pin</code> easier to understand: weak
references. <code class="language-plaintext highlighter-rouge">&amp;weak T</code> would be a new reference kind, with the following properties:</p>
<ul>
  <li>it doesn’t allow any safe accesses;</li>
  <li>while it exists, no contained <code class="language-plaintext highlighter-rouge">!Unpin</code> type is allowed to be moved out of the place; other mutations are fine;</li>
  <li>while it exists the pointed-to place may be deallocated, on the condition that the value is dropped first.</li>
</ul>

<p>So it’s basically a raw pointer, except that while I hold a <code class="language-plaintext highlighter-rouge">&amp;weak T</code> to a place, I know the place
will maintain <em>some</em> consistency. In particular it won’t be deallocated without calling <code class="language-plaintext highlighter-rouge">Drop</code>
first, so the <code class="language-plaintext highlighter-rouge">Drop</code> impl has the opportunity to let me know that my <code class="language-plaintext highlighter-rouge">&amp;weak</code> reference can’t be used
anymore (this is illustrated in the example below).</p>

<p>The way <code class="language-plaintext highlighter-rouge">Pin</code> fits into this is that we can define it as:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="nb">Pin</span><span class="o">&lt;</span><span class="n">P</span><span class="p">:</span> <span class="n">Deref</span><span class="o">&gt;</span><span class="p">(</span><span class="n">P</span><span class="p">,</span> <span class="o">&amp;</span><span class="k">'static</span> <span class="n">weak</span> <span class="nn">P</span><span class="p">::</span><span class="n">Target</span><span class="p">);</span>
</code></pre></div></div>

<p>Here the weirdness of <code class="language-plaintext highlighter-rouge">Pin</code> is made clear: it makes the pointed-to place weakly-borrowed forever.
The API surface and safety requirements of <code class="language-plaintext highlighter-rouge">Pin</code> are all there to make sure only use the <code class="language-plaintext highlighter-rouge">P</code> pointer
in ways that don’t break the invariants imposed by the <code class="language-plaintext highlighter-rouge">&amp;'static weak</code> reference.</p>

<p>Note that it’s ok to take a <code class="language-plaintext highlighter-rouge">'static</code> reference of a local variable here because <code class="language-plaintext highlighter-rouge">&amp;weak</code> allows the
pointed-to place to be deallocated. Of course it can then only be used if you have some mechanism to
know whether the target is still allocated.<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup></p>

<p>Here’s how it would work in an example: intrusive linked lists (based on <a href="https://www.ralfj.de/blog/2018/04/10/safe-intrusive-collections-with-pinning.html">Ralf’s post on the
topic</a>).</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="n">Collection</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="c1">// The `Drop` impl of `Entry` guarantees that the entries listed can be accessed.</span>
    <span class="n">objects</span><span class="p">:</span> <span class="n">RefCell</span><span class="o">&lt;</span><span class="nb">Vec</span><span class="o">&lt;&amp;</span><span class="k">'static</span> <span class="n">weak</span> <span class="n">Entry</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;&gt;&gt;</span><span class="p">,</span>
<span class="p">}</span>
<span class="k">impl</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span> <span class="o">!</span><span class="nb">Unpin</span> <span class="k">for</span> <span class="n">Collection</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span> <span class="p">{}</span>

<span class="k">struct</span> <span class="n">Entry</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="n">x</span><span class="p">:</span> <span class="n">T</span><span class="p">,</span>
    <span class="c1">// Set to `Some` if we are part of some collection.</span>
    <span class="c1">// The `Drop` impl of `Collection` guarantees that the collection can be accessed.</span>
    <span class="n">collection</span><span class="p">:</span> <span class="n">Cell</span><span class="o">&lt;</span><span class="nb">Option</span><span class="o">&lt;&amp;</span><span class="k">'static</span> <span class="n">weak</span> <span class="n">Collection</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;&gt;&gt;</span><span class="p">,</span>
<span class="p">}</span>
<span class="k">impl</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span> <span class="o">!</span><span class="nb">Unpin</span> <span class="k">for</span> <span class="n">Entry</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span> <span class="p">{}</span>

<span class="k">impl</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span> <span class="n">Collection</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="k">fn</span> <span class="nf">new</span><span class="p">()</span> <span class="k">-&gt;</span> <span class="k">Self</span> <span class="p">{</span>
        <span class="n">Collection</span> <span class="p">{</span> <span class="n">objects</span><span class="p">:</span> <span class="nn">RefCell</span><span class="p">::</span><span class="nf">new</span><span class="p">(</span><span class="nn">Vec</span><span class="p">::</span><span class="nf">new</span><span class="p">())</span> <span class="p">}</span>
    <span class="p">}</span>

    <span class="c1">// Add the entry to the collection.</span>
    <span class="k">fn</span> <span class="nf">insert</span><span class="p">(</span><span class="k">mut</span> <span class="k">self</span><span class="p">:</span> <span class="nb">Pin</span><span class="o">&lt;&amp;</span><span class="k">mut</span> <span class="k">Self</span><span class="o">&gt;</span><span class="p">,</span> <span class="n">entry</span><span class="p">:</span> <span class="nb">Pin</span><span class="o">&lt;&amp;</span><span class="k">mut</span> <span class="n">Entry</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;&gt;</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">if</span> <span class="n">entry</span><span class="py">.collection</span><span class="nf">.get</span><span class="p">()</span><span class="nf">.is_some</span><span class="p">()</span> <span class="p">{</span>
            <span class="nd">panic!</span><span class="p">(</span><span class="s">"Can't insert the same object into multiple collections"</span><span class="p">);</span>
        <span class="p">}</span>
        <span class="c1">// Pointer from collection to entry. This `&amp;mut` is unsafe: not all mutations</span>
        <span class="c1">// through it are allowed.</span>
        <span class="k">let</span> <span class="n">mut_this</span> <span class="p">:</span> <span class="o">&amp;</span><span class="k">mut</span> <span class="k">Self</span> <span class="o">=</span> <span class="k">unsafe</span> <span class="p">{</span> <span class="nn">Pin</span><span class="p">::</span><span class="nf">get_mut</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span> <span class="k">self</span><span class="p">)</span> <span class="p">};</span>
        <span class="n">mut_this</span><span class="py">.objects</span><span class="nf">.borrow_mut</span><span class="p">()</span><span class="nf">.push</span><span class="p">(</span><span class="nn">Pin</span><span class="p">::</span><span class="nf">get_weak</span><span class="p">(</span><span class="o">&amp;</span><span class="n">entry</span><span class="p">));</span>
        <span class="c1">// Pointer from entry to collection.</span>
        <span class="k">let</span> <span class="n">weak_this</span><span class="p">:</span> <span class="o">&amp;</span><span class="n">weak</span> <span class="k">Self</span> <span class="o">=</span> <span class="nn">Pin</span><span class="p">::</span><span class="nf">get_weak</span><span class="p">(</span><span class="o">&amp;</span><span class="k">self</span><span class="p">);</span>
        <span class="n">entry</span><span class="py">.collection</span><span class="nf">.set</span><span class="p">(</span><span class="nf">Some</span><span class="p">(</span><span class="n">weak_this</span><span class="p">));</span>
    <span class="p">}</span>

    <span class="c1">// Show all entries of the collection.</span>
    <span class="k">fn</span> <span class="nf">print_all</span><span class="p">(</span><span class="k">self</span><span class="p">:</span> <span class="nb">Pin</span><span class="o">&lt;&amp;</span><span class="k">mut</span> <span class="k">Self</span><span class="o">&gt;</span><span class="p">)</span>
    <span class="k">where</span> <span class="n">T</span><span class="p">:</span> <span class="n">Debug</span>
    <span class="p">{</span>
        <span class="nd">print!</span><span class="p">(</span><span class="s">"["</span><span class="p">);</span>
        <span class="k">for</span> <span class="n">entry</span> <span class="k">in</span> <span class="k">self</span><span class="py">.objects</span><span class="nf">.borrow</span><span class="p">()</span><span class="nf">.iter</span><span class="p">()</span> <span class="p">{</span>
            <span class="c1">// Safety: the `&amp;weak` ref guarantees:</span>
            <span class="c1">// 1. that `entry.collection` cannot be changed and keeps pointing to this</span>
            <span class="c1">//   collection;</span>
            <span class="c1">// 2. that the entry won't be deallocated without running `Drop`.</span>
            <span class="c1">// The `Drop` impl of `Entry` will remove itself from its `entry.collection`,</span>
            <span class="c1">// so combined with the guarantees above we know that the weak refs we hold</span>
            <span class="c1">// here can be used.</span>
            <span class="k">let</span> <span class="n">entry</span> <span class="p">:</span> <span class="o">&amp;</span><span class="n">Entry</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span> <span class="o">=</span> <span class="k">unsafe</span> <span class="p">{</span> <span class="o">&amp;**</span><span class="n">entry</span> <span class="p">};</span>
            <span class="nd">print!</span><span class="p">(</span><span class="s">" {:?},"</span><span class="p">,</span> <span class="n">entry</span><span class="py">.x</span><span class="p">);</span>
        <span class="p">}</span>
        <span class="nd">println!</span><span class="p">(</span><span class="s">" ]"</span><span class="p">);</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="k">impl</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span> <span class="nb">Drop</span> <span class="k">for</span> <span class="n">Collection</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="k">fn</span> <span class="nf">drop</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span> <span class="k">self</span><span class="p">)</span> <span class="p">{</span>
        <span class="c1">// Go through the entries to remove pointers to the collection.</span>
        <span class="k">for</span> <span class="n">entry</span> <span class="k">in</span> <span class="k">self</span><span class="py">.objects</span><span class="nf">.borrow</span><span class="p">()</span><span class="nf">.iter</span><span class="p">()</span> <span class="p">{</span>
            <span class="c1">// Safety: the `&amp;weak` ref guarantees:</span>
            <span class="c1">// 1. that `entry.collection` cannot be changed and keeps pointing to this</span>
            <span class="c1">//   collection;</span>
            <span class="c1">// 2. that the entry won't be deallocated without running `Drop`.</span>
            <span class="c1">// The `Drop` impl of `Entry` will remove itself from its `entry.collection`,</span>
            <span class="c1">// so combined with the guarantees above we know that the weak refs we hold</span>
            <span class="c1">// here can be used.</span>
            <span class="k">let</span> <span class="n">entry</span> <span class="p">:</span> <span class="o">&amp;</span><span class="n">Entry</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span> <span class="o">=</span> <span class="k">unsafe</span> <span class="p">{</span> <span class="o">&amp;**</span><span class="n">entry</span> <span class="p">};</span>
            <span class="n">entry</span><span class="py">.collection</span><span class="nf">.set</span><span class="p">(</span><span class="nb">None</span><span class="p">);</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="k">impl</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span> <span class="n">Entry</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="k">fn</span> <span class="nf">new</span><span class="p">(</span><span class="n">x</span><span class="p">:</span> <span class="n">T</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="k">Self</span> <span class="p">{</span>
        <span class="n">Entry</span> <span class="p">{</span> <span class="n">x</span><span class="p">,</span> <span class="n">collection</span><span class="p">:</span> <span class="nn">Cell</span><span class="p">::</span><span class="nf">new</span><span class="p">(</span><span class="nb">None</span><span class="p">)</span> <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="k">impl</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span> <span class="nb">Drop</span> <span class="k">for</span> <span class="n">Entry</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="k">fn</span> <span class="nf">drop</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span> <span class="k">self</span><span class="p">)</span> <span class="p">{</span>
        <span class="c1">// Go through collection to remove this entry.</span>
        <span class="k">if</span> <span class="k">let</span> <span class="nf">Some</span><span class="p">(</span><span class="n">collection</span><span class="p">)</span> <span class="o">=</span> <span class="k">self</span><span class="py">.collection</span><span class="nf">.get</span><span class="p">()</span> <span class="p">{</span>
            <span class="c1">// Safety: the `&amp;weak` ref guarantees:</span>
            <span class="c1">// 1. that `collection.objects` cannot be changed without cooperation from</span>
            <span class="c1">//   the `Collection` API;</span>
            <span class="c1">// 2. that the collection won't be deallocated without running `Drop`.</span>
            <span class="c1">// The `Drop` impl of `Collection` will remove itself from all the entries</span>
            <span class="c1">// it contains, so combined with the guarantees above we know that the</span>
            <span class="c1">// weak ref we hold here can be used.</span>
            <span class="k">let</span> <span class="n">collection</span> <span class="p">:</span> <span class="o">&amp;</span><span class="n">Collection</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span> <span class="o">=</span> <span class="k">unsafe</span> <span class="p">{</span> <span class="o">&amp;*</span><span class="n">collection</span> <span class="p">};</span>
            <span class="n">collection</span><span class="py">.objects</span><span class="nf">.borrow_mut</span><span class="p">()</span><span class="nf">.retain</span><span class="p">(|</span><span class="n">ptr</span><span class="p">|</span> <span class="n">ptr</span><span class="nf">.addr</span><span class="p">()</span> <span class="o">!=</span> <span class="k">self</span><span class="nf">.addr</span><span class="p">());</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Here the collection keeps a <code class="language-plaintext highlighter-rouge">&amp;weak</code> reference to each entry, and we rely on the <code class="language-plaintext highlighter-rouge">Drop</code> guarantee of
<code class="language-plaintext highlighter-rouge">&amp;weak</code> for the safety of our API. We could imagine using real lifetimes instead of <code class="language-plaintext highlighter-rouge">'static</code>: the
entry only needs to stay pinned as long as it exists inside the collection. Once we remove an entry,
its <code class="language-plaintext highlighter-rouge">&amp;weak</code> reference goes out of scope, so we could in theory go back to doing whatever we want
with the entry.<sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">2</a></sup></p>

<p>Of note, which wasn’t obvious to me, is that the collection too needs to be pinned. That’s because
the entries need to keep a pointer to it. This highlights the two ingredients for a safe <code class="language-plaintext highlighter-rouge">Pin</code>-based
API:</p>
<ul>
  <li>a lifetimeless pointer to a place;</li>
  <li>a mechanism for the <code class="language-plaintext highlighter-rouge">Drop</code> impl of the pinned type to make sure we don’t use the pointer any
longer.</li>
</ul>

<p>This typically means a reference cycle, but that’s not necessary. The following example doesn’t
involve a reference cycle but requires pinning:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="n">A</span><span class="o">&lt;</span><span class="nv">'a</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="c1">// While this is `true`, `ptr` is valid for accesses.</span>
    <span class="n">is_ptr_valid</span><span class="p">:</span> <span class="o">&amp;</span><span class="nv">'a</span> <span class="n">AtomicBool</span><span class="p">,</span>
    <span class="n">ptr</span><span class="p">:</span> <span class="o">&amp;</span><span class="k">'static</span> <span class="n">weak</span> <span class="n">B</span><span class="o">&lt;</span><span class="nv">'a</span><span class="o">&gt;</span><span class="p">,</span>
<span class="p">}</span>
<span class="k">struct</span> <span class="n">B</span><span class="o">&lt;</span><span class="nv">'a</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="n">flag</span><span class="p">:</span> <span class="o">&amp;</span><span class="nv">'a</span> <span class="n">AtomicBool</span><span class="p">,</span>
    <span class="n">some_data</span><span class="p">:</span> <span class="n">Data</span><span class="p">,</span>
<span class="p">}</span>
<span class="k">impl</span> <span class="o">!</span><span class="nb">Unpin</span> <span class="k">for</span> <span class="n">B</span><span class="o">&lt;</span><span class="nv">'_</span><span class="o">&gt;</span> <span class="p">{}</span>

<span class="k">impl</span> <span class="nb">Drop</span> <span class="k">for</span> <span class="n">B</span><span class="o">&lt;</span><span class="nv">'_</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="k">fn</span> <span class="nf">drop</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span> <span class="k">self</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">self</span><span class="py">.flag</span><span class="nf">.store</span><span class="p">(</span><span class="k">false</span><span class="p">,</span> <span class="nn">Ordering</span><span class="p">::</span><span class="n">Relaxed</span><span class="p">);</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>We can also understand the need for <code class="language-plaintext highlighter-rouge">!Unpin</code>: without it, one could swap two <code class="language-plaintext highlighter-rouge">Entry</code>s, which would
make their <code class="language-plaintext highlighter-rouge">collection</code> field no longer point to the right thing. In some way the “cause” of <code class="language-plaintext highlighter-rouge">Entry:
!Unpin</code> is the contained <code class="language-plaintext highlighter-rouge">&amp;weak Collection</code>; so when <code class="language-plaintext highlighter-rouge">entry.collection.is_none()</code> we could in theory
safely move the entry around.<sup id="fnref:3" role="doc-noteref"><a href="#fn:3" class="footnote" rel="footnote">3</a></sup> Similarly the reason that <code class="language-plaintext highlighter-rouge">B</code> is <code class="language-plaintext highlighter-rouge">!Unpin</code> is to ensure the
<code class="language-plaintext highlighter-rouge">&amp;AtomicBool</code> doesn’t get swapped out while <code class="language-plaintext highlighter-rouge">B</code> is weakly borrowed.</p>

<p>So here we go, I personally found this enlightening: <code class="language-plaintext highlighter-rouge">Pin&lt;P&gt;</code> is basically a way to manage a particular
kind of untracked static borrow in a safe API. When I have a <code class="language-plaintext highlighter-rouge">Pin&lt;P&gt;</code> I can store the attached
weak reference wherever I want, and holding such a weak reference limits what can happen to the
place just enough that we can keep things consistent and safe.<sup id="fnref:4" role="doc-noteref"><a href="#fn:4" class="footnote" rel="footnote">4</a></sup></p>

<p>Let me know if you found this mental model helpful!</p>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1" role="doc-endnote">
      <p>I wonder if this can be implemented consistently in the language: lifetimes are normally linked to loans of a place, but here the loan could outlive the place? I hope that doesn’t break anything else. This is definitely a weird kind of reference. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:2" role="doc-endnote">
      <p>This is not something that can be tracked by today’s borrow-checker though. Removing an item from a <code class="language-plaintext highlighter-rouge">Vec&lt;Foo&lt;'a&gt;&gt;</code> doesn’t change the type of the collection so the borrow-checker can’t know that the lifetime can be ended now. <a href="#fnref:2" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:3" role="doc-endnote">
      <p>Wouldn’t it be cute if we could express things like that in the trait system? Or maybe terrifying. I can’t decide. <a href="#fnref:3" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:4" role="doc-endnote">
      <p>I can’t help but be impressed at how neatly this works, this feels like a tour de force. I vaguely remember lengthy discussions at the time; it was not clear at all whether something like this would be feasible! So kudos to everyone involved. <a href="#fnref:4" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Nadri</name></author><summary type="html"><![CDATA[Disclaimer: I am by no means a Pin expert so this may be incorrect in subtle ways. Please let me know if so!]]></summary></entry><entry><title type="html">Flexible Explicit API Knobs</title><link href="https://nadrieril.github.io/blog/2025/11/12/flexible-explicit-api-knobs.html" rel="alternate" type="text/html" title="Flexible Explicit API Knobs" /><published>2025-11-12T09:43:00+00:00</published><updated>2025-11-12T09:43:00+00:00</updated><id>https://nadrieril.github.io/blog/2025/11/12/flexible-explicit-api-knobs</id><content type="html" xml:base="https://nadrieril.github.io/blog/2025/11/12/flexible-explicit-api-knobs.html"><![CDATA[<p>A ton of t-lang design decisions hinge on thinking about “how can this code evolve”, “what am I promising my users by writing this code”, etc. And a lot of feature bikeshedding is about choosing sane defaults so crate authors know what they’re committing to while still allowing flexibility.</p>

<p>I propose we could make this a more explicit part of the language by giving crate authors a common
language to control their present API surface and opt-in/opt-out of future API changes.</p>

<p>The proposed keyword choices are very much not what I expect to be accepted; I’m just trying to
share the idea for now. Please share other compelling examples! (I will edit this blog post to
include more examples).</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Equivalent to `#[non_exhaustive]` on enums.</span>
<span class="nd">#[semver_compatible(allow(author,</span> <span class="nd">add_variants)]</span>
<span class="k">enum</span> <span class="n">Enum</span> <span class="p">{</span> <span class="o">...</span> <span class="p">}</span>

<span class="k">enum</span> <span class="n">Enum</span> <span class="p">{</span>
    <span class="c1">// Kinda opposite of `#[non_exhaustive]`; forbids user from matching or</span>
    <span class="c1">// constructing this variant.</span>
    <span class="nd">#[semver_compatible(allow(author,</span> <span class="nd">remove_variant)]</span>
    <span class="n">Variant1</span><span class="p">,</span>
    <span class="o">..</span>
<span class="p">}</span>

<span class="c1">// Equivalent to `#[non_exhaustive]` on structs.</span>
<span class="nd">#[semver_compatible(allow(author,</span> <span class="nd">add_fields)]</span>
<span class="k">struct</span> <span class="n">Struct</span> <span class="p">{</span> <span class="o">...</span> <span class="p">}</span>

<span class="c1">// The author can add fields but only public ones. This means downstream</span>
<span class="c1">// crates can use FRU (idea from scottmcm, ty!).</span>
<span class="nd">#[semver_compatible(allow(author,</span> <span class="nd">add_fields(pub))]</span>
<span class="k">struct</span> <span class="n">Struct</span> <span class="p">{</span> <span class="o">...</span> <span class="p">}</span>

<span class="c1">// The author can add fields but only with default values. This means downstream</span>
<span class="c1">// crates can construct it (idea from scottmcm, ty!).</span>
<span class="nd">#[semver_compatible(allow(author,</span> <span class="nd">add_fields(with_defaults))]</span>
<span class="k">struct</span> <span class="n">Struct</span> <span class="p">{</span> <span class="o">...</span> <span class="p">}</span>

<span class="c1">// Allows downstream crates to rely on the layout of this struct. Could be used</span>
<span class="c1">// for safe transmutation to reason about API/ABI stability.</span>
<span class="nd">#[semver_compatible(forbid(author,</span> <span class="nd">change_layout))]</span>
<span class="nd">#[repr(C)]</span>
<span class="k">struct</span> <span class="n">Struct</span> <span class="p">{</span> <span class="o">...</span> <span class="p">}</span>

<span class="c1">// Bound the size of the struct.</span>
<span class="nd">#[semver_compatible(size</span> <span class="err">&lt;</span><span class="nd">=</span> <span class="mi">42</span><span class="nd">)]</span>
<span class="k">struct</span> <span class="n">Struct</span> <span class="p">{</span> <span class="o">...</span> <span class="p">}</span>

<span class="c1">// Commit to keeping these auto traits implemented.</span>
<span class="nd">#[semver_compatible(implements(Send))]</span>
<span class="nd">#[semver_compatible(implements(const</span> <span class="nd">Destruct))]</span>
<span class="k">struct</span> <span class="n">Struct</span> <span class="p">{</span> <span class="o">...</span> <span class="p">}</span>

<span class="k">struct</span> <span class="n">Struct</span> <span class="p">{</span>
    <span class="c1">// Forbid mutation by downstream crates. See also</span>
    <span class="c1">// https://rust-lang.github.io/rfcs/3323-restrictions.html .</span>
    <span class="c1">// This isn't a semver kind of thing so I picked another keyword but I don't like it much.</span>
    <span class="nd">#[api(forbid(downstream,</span> <span class="nd">write)]</span>
    <span class="k">pub</span> <span class="n">x</span><span class="p">:</span> <span class="n">Data</span><span class="p">,</span>
<span class="p">}</span>

<span class="c1">// Prevents downstream crates from implementing this trait. Basically builtin "sealed traits", see</span>
<span class="c1">// also https://rust-lang.github.io/rfcs/3323-restrictions.html .</span>
<span class="c1">// This isn't a semver kind of thing so I picked another keyword but I don't like it much.</span>
<span class="nd">#[api(forbid(downstream,</span> <span class="nd">impl)]</span>
<span class="k">trait</span> <span class="n">Trait</span> <span class="p">{</span> <span class="o">...</span> <span class="p">}</span>

<span class="c1">// Ensures a trait is and stays dyn safe.</span>
<span class="nd">#[semver_compatible(is_dyn_safe)]</span>
<span class="k">trait</span> <span class="n">Trait</span> <span class="p">{</span> <span class="o">...</span> <span class="p">}</span>

<span class="c1">// Prevents adding a new method if it's not const.</span>
<span class="c1">// IIUC, should be enough to allow `const Trait` bounds. If so, that's an</span>
<span class="c1">// alternative to the `const trait Trait` syntax.</span>
<span class="nd">#[semver_compatible(forbid(author,</span> <span class="nd">add_method(not_const))]</span>
<span class="k">trait</span> <span class="n">Trait</span> <span class="p">{</span> <span class="o">...</span> <span class="p">}</span>

<span class="k">trait</span> <span class="n">Trait</span> <span class="p">{</span>
    <span class="c1">// Prevents downstream crates from overriding this method. Replaces `final`</span>
    <span class="c1">// methods (https://github.com/rust-lang/rfcs/pull/3678).</span>
    <span class="nd">#[api(forbid(downstream,</span> <span class="nd">override)]</span>
    <span class="k">fn</span> <span class="nf">method</span><span class="p">()</span> <span class="p">{</span> <span class="o">...</span> <span class="p">}</span>
    
    <span class="c1">// Prevents downstream crates from calling this method.</span>
    <span class="nd">#[api(forbid(downstream,</span> <span class="nd">call)]</span>
    <span class="k">fn</span> <span class="nf">method</span><span class="p">()</span> <span class="p">{</span> <span class="o">...</span> <span class="p">}</span>
<span class="p">}</span>


<span class="c1">// Ensures the lifetime/type param stays covariant.</span>
<span class="nd">#[semver_compatible(covariant(</span><span class="err">'</span><span class="nd">a))]</span>
<span class="nd">#[semver_compatible(covariant(T))]</span>
<span class="k">struct</span> <span class="n">Foo</span><span class="o">&lt;</span><span class="nv">'a</span><span class="p">,</span> <span class="n">T</span><span class="o">&gt;</span> <span class="p">{</span> <span class="o">...</span> <span class="p">}</span>

<span class="c1">// Prevents `foo::&lt;...&gt;` syntax as the generic parameters may change, e.g. going to `impl Trait` instead of an explicit param.</span>
<span class="nd">#[semver_compatible(allow(author,</span> <span class="nd">change_generics))]</span>
<span class="k">fn</span> <span class="n">foo</span><span class="o">&lt;</span><span class="n">T</span><span class="p">:</span> <span class="n">Trait</span><span class="o">&gt;</span><span class="p">(</span><span class="o">..</span><span class="p">)</span> <span class="p">{</span> <span class="o">...</span> <span class="p">}</span>

<span class="c1">// Ensures the generated coroutine implements `Send`.</span>
<span class="nd">#[semver_compatible(implements(Send))]</span>
<span class="k">async</span> <span class="k">fn</span> <span class="nf">foo</span><span class="p">()</span> <span class="p">{</span> <span class="o">...</span> <span class="p">}</span>
</code></pre></div></div>

<p>I would imagine, in a future edition, having a lint that warns if you’re relying on un-committed-to
API facts, e.g. you use <code class="language-plaintext highlighter-rouge">dyn Trait</code> for a trait that didn’t guarantee it would stay dyn-safe.</p>

<p>I feel like there are quite a number of features that would fit in this, and like we could find
a nice common language to talk about those things. This is a call for contributions: do you see
other examples?</p>]]></content><author><name>Nadri</name></author><summary type="html"><![CDATA[A ton of t-lang design decisions hinge on thinking about “how can this code evolve”, “what am I promising my users by writing this code”, etc. And a lot of feature bikeshedding is about choosing sane defaults so crate authors know what they’re committing to while still allowing flexibility.]]></summary></entry></feed>