<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
    <channel>
      <title>Ruminations of a Programmer</title>
      <link>https://debasishg.github.io</link>
      <description>Programming, some math and technical deep dives</description>
      <generator>Zola</generator>
      <language>en</language>
      <atom:link href="https://debasishg.github.io/rss.xml" rel="self" type="application/rss+xml"/>
      <lastBuildDate>Mon, 20 Jul 2026 00:00:00 +0000</lastBuildDate>
      <item>
          <title>Zero-Copy on the Hot Path: Reserve&#x2F;Commit and Fast-Path&#x2F;Slow-Path Splitting</title>
          <pubDate>Mon, 20 Jul 2026 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://debasishg.github.io/blog/part4-zero-copy-reserve-commit-and-fast-slow-path-splitting/</link>
          <guid>https://debasishg.github.io/blog/part4-zero-copy-reserve-commit-and-fast-slow-path-splitting/</guid>
          <description xml:base="https://debasishg.github.io/blog/part4-zero-copy-reserve-commit-and-fast-slow-path-splitting/">&lt;h1 id=&quot;zero-copy-on-the-hot-path-reserve-commit-and-fast-path-slow-path-splitting&quot;&gt;Zero-Copy on the Hot Path: Reserve&#x2F;Commit and Fast-Path&#x2F;Slow-Path Splitting&lt;&#x2F;h1&gt;
&lt;p&gt;Part 4 of &lt;strong&gt;Low-Level Systems Design in Rust&lt;&#x2F;strong&gt; - a series on writing high-throughput, low-latency systems code, using a single-producer &#x2F; single-consumer (SPSC) ring buffer as the running example.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;debasishg.github.io&#x2F;blog&#x2F;part1-cache-conscious-data-layout-in-rust&#x2F;&quot;&gt;Part 1&lt;&#x2F;a&gt; decided &lt;strong&gt;where&lt;&#x2F;strong&gt; the shared cursors of a concurrent structure live in memory.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;debasishg.github.io&#x2F;blog&#x2F;part2-cross-core-contract-memory-ordering-and-single-writer-state&#x2F;&quot;&gt;Part 2&lt;&#x2F;a&gt; covered &lt;strong&gt;how&lt;&#x2F;strong&gt; two cores read and write them correctly and at minimum cost.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;debasishg.github.io&#x2F;blog&#x2F;part3-amortizing-cross-core-coordination-caching-and-batching&#x2F;&quot;&gt;Part 3&lt;&#x2F;a&gt; made those reads and writes &lt;em&gt;rare&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;This post is about the shape of the operation itself - moving data without copying it, and structuring each call so the common case is a branch the CPU can predict and pipeline. The running example is still a single-producer &#x2F; single-consumer (SPSC) ring buffer, but neither
technique is specific to ring buffers.&lt;&#x2F;p&gt;
&lt;p&gt;There are code snippets as part of the post. If you want to take a deeper dive into the ring buffer project, take a look at the code, tests and Quint specifications at the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;debasishg&#x2F;ringmpsc-rs&#x2F;tree&#x2F;main&#x2F;crates&#x2F;ringmpsc&quot;&gt;repo&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;two-taxes-that-have-nothing-to-do-with-atomics&quot;&gt;Two taxes that have nothing to do with atomics&lt;&#x2F;h2&gt;
&lt;p&gt;Parts 1 through 3 were all about the cost of &lt;em&gt;coordination&lt;&#x2F;em&gt; - cache lines, memory ordering, and how often cores have to talk. But a naive hot path pays two more taxes that no amount of clever atomics will fix:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Copying.&lt;&#x2F;strong&gt; In the obvious queue API the producer builds an item somewhere (on its stack), then &lt;code&gt;push(item)&lt;&#x2F;code&gt; copies it into a ring slot - that&#x27;s the &lt;em&gt;copy-in&lt;&#x2F;em&gt;. Later the consumer calls &lt;code&gt;pop()&lt;&#x2F;code&gt;, which returns the item &lt;em&gt;by value&lt;&#x2F;em&gt;, meaning it gets copied back out of the slot into the consumer&#x27;s local variable - that&#x27;s the &lt;em&gt;copy-out&lt;&#x2F;em&gt;. For anything larger than a machine word that&#x27;s two &lt;code&gt;memcpy&lt;&#x2F;code&gt;s per item the design can avoid. This post presents the reserve&#x2F;commit protocol that builds the producer side, which eliminates the copy-in. The consumer&#x27;s copy-out is really the mirror-image protocol - borrow the initialized slots in place, process them, then release.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Mixing the rare case with the common one.&lt;&#x2F;strong&gt; If every call runs the expensive path (synchronize, allocate, syscall) inline with the cheap one, the branch predictor can&#x27;t learn the common case and the CPU can&#x27;t pipeline past it.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;This post removes both: &lt;strong&gt;zero-copy via reserve&#x2F;commit&lt;&#x2F;strong&gt;, then &lt;strong&gt;fast-path&#x2F;slow-path splitting&lt;&#x2F;strong&gt;.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;technique-1-zero-copy-via-reserve-commit&quot;&gt;Technique 1 - Zero-copy via reserve&#x2F;commit&lt;&#x2F;h2&gt;
&lt;p&gt;The principle is simple: &lt;em&gt;the owner of the destination memory should write directly into it.&lt;&#x2F;em&gt; Instead of building an item on the stack and handing it to the queue to copy, let the producer construct it in place, inside the slot it will live in. That turns a write-then-copy into a single write.&lt;&#x2F;p&gt;
&lt;p&gt;Mechanically, sending splits into two phases:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Reserve&lt;&#x2F;strong&gt; - the queue advances its cursor to claim space and hands back a mutable slice of &lt;em&gt;uninitialized&lt;&#x2F;em&gt; slots: &lt;code&gt;&amp;amp;mut [MaybeUninit&amp;lt;T&amp;gt;]&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Commit&lt;&#x2F;strong&gt; - the producer signals &quot;I&#x27;ve initialized the first &lt;code&gt;K&lt;&#x2F;code&gt; slots; publish them&quot; - and &lt;em&gt;that&lt;&#x2F;em&gt; store is the Release publication from Part 2.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Here is the protocol end to end. The thing to watch is the window between reserve and commit: the slots exist and the producer is writing into them, but nothing is visible to the consumer until the Release store closes it.&lt;&#x2F;p&gt;
&lt;pre class=&quot;mermaid&quot;&gt;
  
  sequenceDiagram
    participant P as Producer
    participant R as Ring slots
    participant C as Consumer
    P-&gt;&gt;R: reserve(n) - claim space
    R--&gt;&gt;P: &amp;amp;mut [MaybeUninit&amp;lt;T&amp;gt;]
    Note over P,R: uninitialized window opens -&lt;br&#x2F;&gt;slots claimed, invisible to the consumer
    P-&gt;&gt;R: write slot 0 in place
    P-&gt;&gt;R: write slots 1 .. k-1 in place
    P-&gt;&gt;R: commit() - Release store of cursor
    Note over P,R: window closes - exactly k slots published
    C-&gt;&gt;R: Acquire load of cursor
    R--&gt;&gt;C: k initialized items visible
&lt;&#x2F;pre&gt;
&lt;p&gt;The whole game is in getting the safety of that handoff right, and the type system covers only half of it. &lt;code&gt;MaybeUninit&amp;lt;T&amp;gt;&lt;&#x2F;code&gt; polices the &lt;em&gt;read&lt;&#x2F;em&gt; side: a slot cannot be read as a &lt;code&gt;T&lt;&#x2F;code&gt; without an explicit (and &lt;code&gt;unsafe&lt;&#x2F;code&gt;) &lt;code&gt;assume_init&lt;&#x2F;code&gt;, so you can&#x27;t stumble into garbage by accident. What it cannot police is the &lt;em&gt;write&lt;&#x2F;em&gt; side: a &lt;code&gt;&amp;amp;mut [MaybeUninit&amp;lt;T&amp;gt;]&lt;&#x2F;code&gt; has exactly the same type whether you initialized all &lt;code&gt;n&lt;&#x2F;code&gt; slots, the first &lt;code&gt;k&lt;&#x2F;code&gt;, or none - initialization is a runtime fact the slice type doesn&#x27;t record. So when commit declares &quot;the first &lt;code&gt;k&lt;&#x2F;code&gt; slots are real &lt;code&gt;T&lt;&#x2F;code&gt;s now&quot;, no compiler check can confirm the claim. Closing that gap is what the API shapes below are about.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;a-safe-commit-has-to-be-paid-for&quot;&gt;A safe &lt;code&gt;commit&lt;&#x2F;code&gt; has to be paid for&lt;&#x2F;h3&gt;
&lt;p&gt;Here&#x27;s the trap. If &lt;code&gt;commit()&lt;&#x2F;code&gt; is a safe method that just does &lt;code&gt;tail += n&lt;&#x2F;code&gt; - no bookkeeping, no proof - a caller can commit &lt;em&gt;without&lt;&#x2F;em&gt; having initialized every slot, and then the consumer calls &lt;code&gt;assume_init_read&lt;&#x2F;code&gt; on uninitialized memory, which is undefined behavior. So the safety has to be paid for somewhere in the API shape: either &lt;code&gt;commit&lt;&#x2F;code&gt; is &lt;code&gt;unsafe&lt;&#x2F;code&gt; and the &lt;em&gt;caller&lt;&#x2F;em&gt; carries the proof of initialization, or the reservation &lt;em&gt;tracks&lt;&#x2F;em&gt; initialization itself and publishes only what it tracked. There are three sound shapes; pick one and commit to it:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;&#x2F;&#x2F; Option A - commit is `unsafe`; the caller proves every slot is written.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;impl&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;a&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; T&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; A&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; BufferAllocator&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Reservation&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;a&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; T&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; A&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    pub fn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; as_mut_slice&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-storage&quot;&gt;&amp;amp;mut&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language z-self&quot;&gt; self&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-storage&quot;&gt; -&amp;gt; &amp;amp;mut&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; [&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;MaybeUninit&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;T&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;gt;];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;    &#x2F;&#x2F;&#x2F; SAFETY: every slot from `as_mut_slice()` was initialized via&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;    &#x2F;&#x2F;&#x2F; `MaybeUninit::write`.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    pub unsafe fn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; commit&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language z-self&quot;&gt;self&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;&#x2F;&#x2F; Option B - the caller passes the initialized count; commit is `unsafe`&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;&#x2F;&#x2F;            because the API cannot verify that count itself.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;impl&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;a&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; T&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; A&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; BufferAllocator&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Reservation&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;a&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; T&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; A&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;    &#x2F;&#x2F;&#x2F; SAFETY: the first `initialized` slots are fully initialized.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    pub unsafe fn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; commit_initialized&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language z-self&quot;&gt;self&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-variable&quot;&gt;, initialized&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; usize&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;&#x2F;&#x2F; Option C - safe builder. The reservation owns the init counter and&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;&#x2F;&#x2F;            publishes exactly what it tracked. Zero `unsafe` at the call site.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;impl&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;a&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; T&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; A&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; BufferAllocator&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Reservation&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;a&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; T&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; A&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    pub fn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; push&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-storage&quot;&gt;&amp;amp;mut&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language z-self&quot;&gt; self&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-variable&quot;&gt;, value&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; T&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Result&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;(),&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; T&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt; &#x2F;&#x2F; bumps `written`&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    pub fn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; commit&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language z-self&quot;&gt;self&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;);&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;                               &#x2F;&#x2F; safe&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;Option C is the right default for a public API&lt;&#x2F;strong&gt;: the safety proof lives inside the type, once, instead of in every caller. Options A and B justify their &lt;code&gt;unsafe&lt;&#x2F;code&gt; only when the caller does &lt;em&gt;bulk&lt;&#x2F;em&gt; initialization - a SIMD copy or &lt;code&gt;copy_from_slice&lt;&#x2F;code&gt; over the whole slice - where threading a per-item counter would cost more than the discipline saves.&lt;&#x2F;p&gt;
&lt;p&gt;With the safe builder, the call site has no &lt;code&gt;unsafe&lt;&#x2F;code&gt; at all:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;if&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt; let&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Some&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage&quot;&gt;mut&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt; r)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; producer&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;reserve&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-variable&quot;&gt;(n) {&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;        &#x2F;&#x2F; &amp;amp;mut self - see Part 2&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    for&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; i&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; in&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;..&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;r&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;capacity&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;() {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        if&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; r&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;push&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;make_item&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-variable&quot;&gt;(i))&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;is_err&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;() {&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; break&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;; }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    r&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;commit&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;();&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;                                   &#x2F;&#x2F; publishes only what was written&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The unsafe form trades that safety for the ability to write the slots however you
like:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;if&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt; let&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Some&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage&quot;&gt;mut&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt; r)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; producer&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;reserve&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-variable&quot;&gt;(n) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;    let&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; slice&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; r&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;as_mut_slice&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;();&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;                 &#x2F;&#x2F; &amp;amp;mut [MaybeUninit&amp;lt;T&amp;gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    for&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; slot&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; in&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; slice&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;iter_mut&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;() {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;        slot&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;write&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;make_item&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;());&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;                  &#x2F;&#x2F; .write(), NOT assignment through as_mut_ptr()&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;    &#x2F;&#x2F; SAFETY: the loop above initialized every slot in the slice.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    unsafe&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-variable&quot;&gt; { r&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;commit&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(); }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;That &lt;code&gt;slot.write(value)&lt;&#x2F;code&gt; is not a stylistic choice. Consider the tempting alternatives: &lt;code&gt;*slot.as_mut_ptr() = value&lt;&#x2F;code&gt;, or &lt;code&gt;*slot.assume_init_mut() = value&lt;&#x2F;code&gt;. Both are ordinary assignments through a &lt;code&gt;T&lt;&#x2F;code&gt;-typed place. And an ordinary assignment &lt;em&gt;drops the previous value first&lt;&#x2F;em&gt;. But here there is no previous value. The destructor runs on uninitialized garbage - undefined behavior. For a &lt;code&gt;String&lt;&#x2F;code&gt; it blows up loudly. For a type with no drop glue it stays silent - until a refactor gives it some. &lt;code&gt;MaybeUninit::write&lt;&#x2F;code&gt; initializes the slot without running any prior drop. (&lt;code&gt;*slot = MaybeUninit::new(value)&lt;&#x2F;code&gt; is also fine - &lt;code&gt;MaybeUninit&lt;&#x2F;code&gt; itself has no drop glue - but &lt;code&gt;.write()&lt;&#x2F;code&gt; says what you mean.) No mistake introduces UB into a reserve&#x2F;commit loop more often than this one.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;panic-safety-disarm-the-destructor-before-you-publish&quot;&gt;Panic safety: disarm the destructor &lt;em&gt;before&lt;&#x2F;em&gt; you publish&lt;&#x2F;h3&gt;
&lt;p&gt;Now the subtle bug, the one that survives code review. Suppose &lt;code&gt;make_item&lt;&#x2F;code&gt; panics partway through the loop - some slots written, &lt;code&gt;commit()&lt;&#x2F;code&gt; not yet reached. Two things must hold:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;the partially-written slots must be cleaned up (dropped in place, or leaked) - but they must &lt;strong&gt;not&lt;&#x2F;strong&gt; be published, because no commit happened and the consumer must still see the old cursor&lt;&#x2F;li&gt;
&lt;li&gt;and here&#x27;s the trap: &lt;code&gt;commit(self)&lt;&#x2F;code&gt; takes the reservation &lt;em&gt;by value&lt;&#x2F;em&gt;, which means Rust &lt;strong&gt;still runs &lt;code&gt;Drop&lt;&#x2F;code&gt; on &lt;code&gt;self&lt;&#x2F;code&gt; at the end of &lt;code&gt;commit&lt;&#x2F;code&gt;&#x27;s body&lt;&#x2F;strong&gt;.  If &lt;code&gt;commit&lt;&#x2F;code&gt; advances the cursor and &lt;em&gt;then&lt;&#x2F;em&gt; &lt;code&gt;Drop&lt;&#x2F;code&gt; walks the same slots calling &lt;code&gt;assume_init_drop&lt;&#x2F;code&gt;, those slots get dropped while the consumer also owns them - a classic double-drop.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The fix is two lines: zero the tracked count &lt;em&gt;before&lt;&#x2F;em&gt; publishing, so the destructor that runs at the end of &lt;code&gt;commit&lt;&#x2F;code&gt; finds nothing to do.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;impl&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;a&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; T&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; A&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; BufferAllocator&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Reservation&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;a&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; T&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; A&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;    &#x2F;&#x2F;&#x2F; Safe commit (Option C): publishes exactly the `written` count.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    pub fn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; commit&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage&quot;&gt;mut&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language z-self&quot;&gt; self&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;        let&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; written&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language z-self&quot;&gt; self&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;written;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;        &#x2F;&#x2F; Disarm Drop *before* publishing: from here on the slots belong&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;        &#x2F;&#x2F; to the consumer and the reservation owns nothing.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-language z-self&quot;&gt;        self&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;written &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;        &#x2F;&#x2F; SAFETY: `written` slots were initialized via `push`.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        unsafe&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language z-self&quot;&gt; self&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;ring&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;as_ref&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;()&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;publish&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-variable&quot;&gt;(written); }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;        &#x2F;&#x2F; `self` is dropped here; Drop sees written == 0 and does nothing.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;impl&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;T&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; A&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; BufferAllocator&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Drop&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; for&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Reservation&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;_&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; T&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; A&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    fn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; drop&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-storage&quot;&gt;&amp;amp;mut&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language z-self&quot;&gt; self&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;        &#x2F;&#x2F; Runs on a panic unwind (uncommitted) AND on the no-op tail of commit().&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        for&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; i&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; in&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;..&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language z-self&quot;&gt;self&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;written {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;            &#x2F;&#x2F; SAFETY: slots [0, written) were initialized via `push` and&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;            &#x2F;&#x2F; have not been published.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;            unsafe&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language z-self&quot;&gt; self&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-variable&quot;&gt;slice[i]&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;assume_init_drop&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(); }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;        }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;A reservation with &lt;code&gt;written == k&lt;&#x2F;code&gt; has exactly two legitimate exits, and one forbidden one. The diagram below shows why the ordering inside &lt;code&gt;commit&lt;&#x2F;code&gt; is the entire fix - disarm first, and the destructor that inevitably runs has nothing left to do:&lt;&#x2F;p&gt;
&lt;pre class=&quot;mermaid&quot;&gt;
  
  flowchart TD
    A[&quot;Reservation alive: written = k&quot;] --&gt; B[&quot;panic during make_item()&quot;]
    A --&gt; C[&quot;commit()&quot;]
    B --&gt; B1[&quot;unwind runs Drop:&lt;br&#x2F;&gt;assume_init_drop on slots [0, k)&quot;]
    B1 --&gt; B2[&quot;nothing published - consumer&lt;br&#x2F;&gt;still sees the old cursor ✓&quot;]
    C --&gt; C1[&quot;written = 0 (disarm Drop)&quot;]
    C1 --&gt; C2[&quot;publish(k) - Release store&quot;]
    C2 --&gt; C3[&quot;Drop at end of commit sees&lt;br&#x2F;&gt;written == 0 → no-op ✓&quot;]
    C -.-&gt;|&quot;mis-ordered commit:&lt;br&#x2F;&gt;publish first, disarm late&quot;| X[&quot;Drop walks the same k slots&lt;br&#x2F;&gt;the consumer now owns → double-drop ✗&quot;]
&lt;&#x2F;pre&gt;
&lt;p&gt;This &quot;disarm the destructor before transferring ownership&quot; is the job &lt;code&gt;ManuallyDrop&lt;&#x2F;code&gt; and &lt;code&gt;mem::forget&lt;&#x2F;code&gt; normally do - but neither fits cleanly here. A type that implements &lt;code&gt;Drop&lt;&#x2F;code&gt; can&#x27;t be partially moved out of, so &lt;code&gt;mem::forget(self)&lt;&#x2F;code&gt; would first need &lt;code&gt;ManuallyDrop&lt;&#x2F;code&gt; gymnastics to extract &lt;code&gt;written&lt;&#x2F;code&gt; and the ring pointer. Zeroing the counter achieves the same disarming in one line. The only way to keep a regression out is a test that panics partway through &lt;code&gt;make_item&lt;&#x2F;code&gt; and asserts each value is dropped exactly once.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;the-boxed-closure-trap-and-a-38-throughput-win&quot;&gt;The boxed-closure trap - and a 38% throughput win&lt;&#x2F;h3&gt;
&lt;p&gt;When you first sketch a &lt;code&gt;Reservation&lt;&#x2F;code&gt;, the natural instinct is to give it a closure to call on commit:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;&#x2F;&#x2F; DON&amp;#39;T - heap allocation + vtable dispatch on every reserve()&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;pub&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt; struct&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Reservation&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;a&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; T&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    slice&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:     &amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;a&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage&quot;&gt; mut&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; [&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;MaybeUninit&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;T&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;gt;],&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    on_commit&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Box&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;dyn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; FnOnce&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;usize&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&amp;gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This puts a &lt;code&gt;Box::new&lt;&#x2F;code&gt; and a dynamic dispatch on &lt;em&gt;every single&lt;&#x2F;em&gt; &lt;code&gt;reserve()&lt;&#x2F;code&gt; call - ~5–10 ns of allocation and an indirect jump - which quietly defeats the entire zero-copy story you just built. The right shape stores a non-null pointer back to the parent structure (lifetime-tied) and calls a concrete method:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;use&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-namespace&quot;&gt; std&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-namespace&quot;&gt;marker&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;PhantomData&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;use&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-namespace&quot;&gt; std&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-namespace&quot;&gt;ptr&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;NonNull&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;pub&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt; struct&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Reservation&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;a&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; T&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; A&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; BufferAllocator&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    slice&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:   &amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;a&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage&quot;&gt; mut&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; [&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;MaybeUninit&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;T&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;gt;],&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    ring&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;    NonNull&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;Ring&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;T&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; A&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;gt;&amp;gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    written&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; usize&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    _borrow&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; PhantomData&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;a&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage&quot;&gt; mut&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Ring&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;T&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; A&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;gt;&amp;gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;    &#x2F;&#x2F; documents the logical borrow&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In one implementation, replacing the boxed closure with this pointer form was the single largest throughput improvement in the project&#x27;s history - about &lt;strong&gt;38%&lt;&#x2F;strong&gt; on the zero-copy benchmark. The lesson generalizes: any time the &quot;trait object&quot; temptation shows up on a hot path, ask whether the lifetime relationship can be encoded so that a plain pointer suffices instead.&lt;&#x2F;p&gt;
&lt;p&gt;Two idioms in that struct are worth calling out:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;NonNull&amp;lt;T&amp;gt;&lt;&#x2F;code&gt; over &lt;code&gt;*const T&lt;&#x2F;code&gt;.&lt;&#x2F;strong&gt; The pointer is known non-null (it came from a reference), so &lt;code&gt;NonNull&lt;&#x2F;code&gt; is the honest type: it documents the invariant and enables the null-pointer niche optimization in enclosing enums.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;PhantomData&amp;lt;&amp;amp;&#x27;a mut Ring&amp;lt;T, A&amp;gt;&amp;gt;&lt;&#x2F;code&gt; even though &lt;code&gt;slice&lt;&#x2F;code&gt; already carries &lt;code&gt;&#x27;a&lt;&#x2F;code&gt;.&lt;&#x2F;strong&gt; It looks redundant, but it documents the &lt;em&gt;logical&lt;&#x2F;em&gt; borrow against the ring and survives refactors that might restructure or remove the slice field later. A one-line insurance policy.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;send-sync-falls-out-for-free-and-that-s-the-design&quot;&gt;&lt;code&gt;Send&lt;&#x2F;code&gt;&#x2F;&lt;code&gt;Sync&lt;&#x2F;code&gt; falls out for free - and that&#x27;s the design&lt;&#x2F;h3&gt;
&lt;p&gt;A reasonable worry on seeing a raw-ish &lt;code&gt;NonNull&amp;lt;Ring&amp;lt;T, A&amp;gt;&amp;gt;&lt;&#x2F;code&gt; in a struct: doesn&#x27;t that risk making &lt;code&gt;Reservation&lt;&#x2F;code&gt; accidentally &lt;code&gt;Send&lt;&#x2F;code&gt;&#x2F;&lt;code&gt;Sync&lt;&#x2F;code&gt;? The opposite is true.  &lt;code&gt;NonNull&lt;&#x2F;code&gt; (like raw pointers) is &lt;code&gt;!Send + !Sync&lt;&#x2F;code&gt;, so its &lt;em&gt;presence&lt;&#x2F;em&gt; means the compiler never infers those auto traits for &lt;code&gt;Reservation&lt;&#x2F;code&gt; at all. To be precise about what protects what: the single-producer invariant is really preserved by the exclusive &lt;code&gt;&amp;amp;&#x27;a mut&lt;&#x2F;code&gt; borrow of the producer - even a &lt;code&gt;Send&lt;&#x2F;code&gt; reservation moved to another thread couldn&#x27;t race the producer, because the borrow pins it until the reservation dies. &lt;code&gt;!Send&lt;&#x2F;code&gt; is defense-in-depth on top of that, keeping the raw pointer from crossing threads even under refactors that loosen the borrow; and the &lt;code&gt;&#x27;a&lt;&#x2F;code&gt; lifetime forbids the reservation from outliving the ring. No &lt;code&gt;unsafe impl&lt;&#x2F;code&gt; needed anywhere. The pattern is: lean on auto-trait inference for safety, and reach for &lt;code&gt;unsafe impl&lt;&#x2F;code&gt; only when you have a positive reason to &lt;em&gt;grant&lt;&#x2F;em&gt; &lt;code&gt;Send&lt;&#x2F;code&gt; or &lt;code&gt;Sync&lt;&#x2F;code&gt; (as Part 2 did for the ring itself).&lt;&#x2F;p&gt;
&lt;h3 id=&quot;reservations-can-shrink-and-slices-stay-contiguous&quot;&gt;Reservations can shrink - and slices stay contiguous&lt;&#x2F;h3&gt;
&lt;p&gt;One ergonomic wrinkle: at the end of a circular buffer, a request for &lt;code&gt;n&lt;&#x2F;code&gt; slots may wrap around. The reservation returns the &lt;em&gt;contiguous&lt;&#x2F;em&gt; run up to the buffer boundary, which can be &lt;strong&gt;fewer than &lt;code&gt;n&lt;&#x2F;code&gt;&lt;&#x2F;strong&gt;. Callers must loop:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust z-storage&quot;&gt;let mut&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; remaining&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt; n;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;while&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; remaining&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; &amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    if&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt; let&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Some&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage&quot;&gt;mut&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt; r)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; producer&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;reserve&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-variable&quot;&gt;(remaining) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;        let&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; got&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; r&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;capacity&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;();&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt; &#x2F;&#x2F; MAY BE &amp;lt; remaining at wrap-around&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        for&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; _&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; in&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;..&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt;got {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;            let&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; _&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; r&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;push&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;next_item&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;());&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt; &#x2F;&#x2F; cannot fail: we stay within capacity()&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;        }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;        r&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;commit&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;();&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;        remaining&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; -=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt; got;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;    }&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; else&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;        &#x2F;&#x2F; backpressure: ring full - back off and retry&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;It&#x27;s tempting to &quot;fix&quot; this by returning &lt;em&gt;two&lt;&#x2F;em&gt; slices (a scatter-gather pair) so a single reserve always satisfies &lt;code&gt;n&lt;&#x2F;code&gt;. Don&#x27;t. Every caller would then have to handle the split case, breaking interop with everything that expects contiguous memory - &lt;code&gt;io::Write&lt;&#x2F;code&gt;, &lt;code&gt;memcpy&lt;&#x2F;code&gt;, SIMD. The contiguous-only rule pushes one bit of complexity (the retry loop) onto the producer and removes a much larger pile from every consumer of the slice. If you want the contract to be self-documenting, name the method for it: &lt;code&gt;reserve_contiguous(n)&lt;&#x2F;code&gt; reads as &quot;may be shorter than &lt;code&gt;n&lt;&#x2F;code&gt;&quot;. (A count-taking &lt;code&gt;commit_written(k)&lt;&#x2F;code&gt; is the Option B spelling from above - pair it only with that API shape, not with the safe builder.)&lt;&#x2F;p&gt;
&lt;h3 id=&quot;you-already-use-this-pattern-vec-spare-capacity-mut&quot;&gt;You already use this pattern: &lt;code&gt;Vec::spare_capacity_mut&lt;&#x2F;code&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;If reserve&#x2F;commit feels exotic, it isn&#x27;t - the standard library exposes the exact same protocol on &lt;code&gt;Vec&lt;&#x2F;code&gt;, for the case where you just need a one-shot buffer to feed a syscall, parser, or decompressor. Save the length, fill the spare region, publish with &lt;code&gt;set_len&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;let&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; old_len&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; buf&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;len&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;();&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;let&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; spare&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;   =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; buf&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;spare_capacity_mut&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;();&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;      &#x2F;&#x2F; &amp;amp;mut [MaybeUninit&amp;lt;u8&amp;gt;]  ← reserve&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;let&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; written&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; read_into&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-variable&quot;&gt;(spare);&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;              &#x2F;&#x2F; initialize spare[..written]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;&#x2F;&#x2F; SAFETY:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;&#x2F;&#x2F; - `written &amp;lt;= spare.len()`,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;&#x2F;&#x2F; - `spare[..written]` is now initialized,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;&#x2F;&#x2F; - `old_len + written &amp;lt;= buf.capacity()` (spare len == capacity - old_len).&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;unsafe&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-variable&quot;&gt; { buf&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;set_len&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-variable&quot;&gt;(old_len&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; +&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt; written); }&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;   &#x2F;&#x2F; ← commit&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;spare_capacity_mut&lt;&#x2F;code&gt; is the reserve step (hand back uninitialized slots &lt;em&gt;after&lt;&#x2F;em&gt; the live elements); &lt;code&gt;set_len&lt;&#x2F;code&gt; is the commit step. &lt;code&gt;set_len&lt;&#x2F;code&gt; is &lt;code&gt;unsafe&lt;&#x2F;code&gt; for the very same reason Option A&#x27;s raw &lt;code&gt;commit&lt;&#x2F;code&gt; is: the API can&#x27;t verify the count you pass. And note the bug this pattern quietly avoids - the tempting shortcut &lt;code&gt;set_len(written)&lt;&#x2F;code&gt; is correct &lt;em&gt;only&lt;&#x2F;em&gt; when &lt;code&gt;old_len == 0&lt;&#x2F;code&gt;; on an already-populated buffer it truncates the live prefix, and &lt;code&gt;set_len&lt;&#x2F;code&gt; won&#x27;t stop you, because it does exactly what you tell it. The lesson reserve&#x2F;commit keeps teaching, in &lt;code&gt;Vec&lt;&#x2F;code&gt; and in the ring alike: &lt;strong&gt;initialization tracking is the hard part, not getting a pointer.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;bonus-skip-the-teardown-when-t-has-no-drop-glue&quot;&gt;Bonus: skip the teardown when &lt;code&gt;T&lt;&#x2F;code&gt; has no drop glue&lt;&#x2F;h3&gt;
&lt;p&gt;A generic container has wildly different destruction costs at &lt;code&gt;Ring&amp;lt;u64&amp;gt;&lt;&#x2F;code&gt; versus &lt;code&gt;Ring&amp;lt;String&amp;gt;&lt;&#x2F;code&gt;. &lt;code&gt;std::mem::needs_drop::&amp;lt;T&amp;gt;()&lt;&#x2F;code&gt; is a &lt;code&gt;const fn&lt;&#x2F;code&gt; that resolves to a compile-time-known bool for a monomorphic &lt;code&gt;T&lt;&#x2F;code&gt;, so you can gate the per-slot walk on it and let the trivial case compile to nothing:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;impl&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;T&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; A&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; BufferAllocator&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Drop&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; for&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Ring&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;T&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; A&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    fn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; drop&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-storage&quot;&gt;&amp;amp;mut&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language z-self&quot;&gt; self&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        if&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; !&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-namespace&quot;&gt;std&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-namespace&quot;&gt;mem&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;needs_drop&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;T&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;gt;() {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;            return&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt; &#x2F;&#x2F; Ring&amp;lt;u64&amp;gt;, Ring&amp;lt;[u8; 16]&amp;gt;, …: no-op.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;        }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;        &#x2F;&#x2F; SAFETY: only the [head, tail) range is initialized.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        unsafe&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language z-self&quot;&gt; self&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;drop_initialized_slots&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(); }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;For &lt;code&gt;Ring&amp;lt;u64&amp;gt;&lt;&#x2F;code&gt; this collapses to an empty &lt;code&gt;drop&lt;&#x2F;code&gt;; for &lt;code&gt;Ring&amp;lt;String&amp;gt;&lt;&#x2F;code&gt; the walk runs. Any time a generic container does per-element teardown over &lt;code&gt;MaybeUninit&lt;&#x2F;code&gt; storage, gate it on &lt;code&gt;needs_drop::&amp;lt;T&amp;gt;()&lt;&#x2F;code&gt; so the trivially-droppable case doesn&#x27;t pay for loop machinery it can&#x27;t use.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;technique-2-fast-path-slow-path-splitting&quot;&gt;Technique 2 - Fast-path&#x2F;slow-path splitting&lt;&#x2F;h2&gt;
&lt;p&gt;Zero-copy removed the per-item &lt;em&gt;copy&lt;&#x2F;em&gt;. Fast&#x2F;slow-path splitting removes the per-item &lt;em&gt;rare-case cost&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The observation: most operations on a healthy data structure succeed without contention, allocation, or a system call. So structure every operation so the 99%-of-calls case is a short, predictable branch over local data, and let the rare case pay for correctness off to the side:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;pub fn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; op&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language z-self&quot;&gt;self&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; &#x2F;* … *&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Result&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;    &#x2F;&#x2F; 1. Cheap argument validation.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    if&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt; invalid {&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; return&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt; early; }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;    &#x2F;&#x2F; 2. Fast path - local data only, no cross-core traffic.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    if&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt; local_check_says_ok {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        return&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Ok&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;do_it&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;());&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;    &#x2F;&#x2F; 3. Slow path - synchronize, refresh caches, retry.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-language z-self&quot;&gt;    self&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;refresh_caches&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;();&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    if&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt; still_no_good {&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; return&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Err&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt;&#x2F;* … *&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;); }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;    Ok&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;do_it&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;())&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;You&#x27;ve already seen a concrete instance of this: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;debasishg.github.io&#x2F;blog&#x2F;part3-amortizing-cross-core-coordination-caching-and-batching&#x2F;&quot;&gt;Part 3&#x27;s &lt;code&gt;reserve()&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; checks the producer-owned cached &lt;code&gt;head&lt;&#x2F;code&gt; on the fast path and issues a cross-core &lt;code&gt;Acquire&lt;&#x2F;code&gt; load &lt;em&gt;only&lt;&#x2F;em&gt; when the cache says it&#x27;s out of room. The caching technique from Part 3 is just this pattern with &quot;the cache&quot; as the local check and &quot;refresh the cache&quot; as the slow path.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Why the split is worth the extra branch.&lt;&#x2F;strong&gt; Branch predictors learn a consistently-taken path within a handful of iterations, after which the fast path is predicted essentially for free. Modern out-of-order cores then speculate &lt;em&gt;past&lt;&#x2F;em&gt; that predicted branch and overlap the whole fast-path computation with surrounding instructions. The slow path still costs what it costs - but it costs that &lt;em&gt;only when actually taken&lt;&#x2F;em&gt;, instead of taxing every call.&lt;&#x2F;p&gt;
&lt;p&gt;The same shape recurs everywhere once you look for it:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;lock-free &lt;code&gt;compare_exchange_weak&lt;&#x2F;code&gt; retry loops (fast: the CAS succeeds; slow: contention, reload and retry);&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;Vec::push&lt;&#x2F;code&gt; (fast: spare capacity remains; slow: reallocate and move);&lt;&#x2F;li&gt;
&lt;li&gt;allocators (fast: a thread-local cache hit; slow: lock the central free-list).&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;Help the compiler lay it out.&lt;&#x2F;strong&gt; Inline the fast path aggressively (&lt;code&gt;#[inline]&lt;&#x2F;code&gt;) and keep the slow path out-of-line so it doesn&#x27;t bloat the hot instruction footprint. The optimizer usually arranges this on its own, but marking the slow-path helper &lt;code&gt;#[cold]&lt;&#x2F;code&gt; nudges it to place that code away from the fast path and to predict the branch into it as not-taken.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;where-parts-1-4-leave-us&quot;&gt;Where Parts 1–4 leave us&lt;&#x2F;h2&gt;
&lt;p&gt;Four posts in, the hot path of a lock-free structure has been shaped from four independent directions:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;its fields are laid out so cores don&#x27;t fight over cache lines (Part 1)&lt;&#x2F;li&gt;
&lt;li&gt;its cross-core reads and writes use the minimum-sufficient ordering (Part 2)&lt;&#x2F;li&gt;
&lt;li&gt;those reads and writes happen as rarely as possible (Part 3)&lt;&#x2F;li&gt;
&lt;li&gt;and each operation copies nothing it doesn&#x27;t have to and branches predictably (Part 4)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;None of these is a micro-optimization in isolation - together they&#x27;re the difference between a structure that &lt;em&gt;looks&lt;&#x2F;em&gt; lock-free and one the hardware actually runs at full speed.&lt;&#x2F;p&gt;
&lt;p&gt;What&#x27;s left is leverage the compiler can give you &lt;em&gt;for free&lt;&#x2F;em&gt; if you ask in the right language: const generics that bake sizes in at compile time, arithmetic and branch hygiene that lets the optimizer prove what it needs, and inlining discipline. We discuss that compile-time leverage in Part 5 - &lt;em&gt;Compile-Time Leverage&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Amortizing Cross-Core Coordination: Cursor Caching and Batch Processing</title>
          <pubDate>Mon, 13 Jul 2026 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://debasishg.github.io/blog/part3-amortizing-cross-core-coordination-caching-and-batching/</link>
          <guid>https://debasishg.github.io/blog/part3-amortizing-cross-core-coordination-caching-and-batching/</guid>
          <description xml:base="https://debasishg.github.io/blog/part3-amortizing-cross-core-coordination-caching-and-batching/">&lt;h1 id=&quot;amortizing-cross-core-coordination-cursor-caching-and-batch-processing&quot;&gt;Amortizing Cross-Core Coordination: Cursor Caching and Batch Processing&lt;&#x2F;h1&gt;
&lt;p&gt;Part 3 of &lt;strong&gt;Low-Level Systems Design in Rust&lt;&#x2F;strong&gt; - a series on writing high-throughput, low-latency systems code, using a single-producer &#x2F; single-consumer (SPSC) ring buffer as the running example.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;debasishg.github.io&#x2F;blog&#x2F;part1-cache-conscious-data-layout-in-rust&#x2F;&quot;&gt;Part 1&lt;&#x2F;a&gt; decided &lt;strong&gt;where&lt;&#x2F;strong&gt; the shared cursors of a concurrent structure live in memory.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;debasishg.github.io&#x2F;blog&#x2F;part2-cross-core-contract-memory-ordering-and-single-writer-state&#x2F;&quot;&gt;Part 2&lt;&#x2F;a&gt; covered &lt;strong&gt;how&lt;&#x2F;strong&gt; two cores read and write them correctly and at minimum cost.&lt;&#x2F;p&gt;
&lt;p&gt;This post is about doing it &lt;strong&gt;less often&lt;&#x2F;strong&gt; - because the cheapest cross-core synchronization is the one you don&#x27;t perform. The running example is still a single-producer &#x2F; single-consumer (SPSC) ring buffer, but the techniques apply to any structure where cores coordinate through shared atomics.&lt;&#x2F;p&gt;
&lt;p&gt;There are code snippets as part of the post. If you want to take a deeper dive into the ring buffer project, take a look at the code, tests and Quint specifications at the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;debasishg&#x2F;ringmpsc-rs&#x2F;tree&#x2F;main&#x2F;crates&#x2F;ringmpsc&quot;&gt;repo&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;the-expensive-operation-and-two-ways-to-spend-less-on-it&quot;&gt;The expensive operation, and two ways to spend less on it&lt;&#x2F;h2&gt;
&lt;p&gt;Part 2 established the unit of cost in lock-free coordination: a cross-core atomic. Just as a refresher in the SPSC ring buffer we have two cursors living in shared memory:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;tail&lt;&#x2F;code&gt; - written by the producer, tracks where it will write next.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;head&lt;&#x2F;code&gt; - written by the consumer, tracks where it will read next.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Modern CPUs don&#x27;t read&#x2F;write main memory (RAM) directly on the hot path - they operate on cache lines (typically 64 bytes) held in each core&#x27;s private L1&#x2F;L2 cache. When the consumer does &lt;code&gt;head.store(...)&lt;&#x2F;code&gt;, that store lands in the consumer core&#x27;s cache, and the cache line holding the &lt;code&gt;head&lt;&#x2F;code&gt; becomes owned exclusively by that core in the Modified (M) state of the MESI coherence protocol.&lt;&#x2F;p&gt;
&lt;p&gt;Now when the producer wants to read the &lt;code&gt;head&lt;&#x2F;code&gt; (to check &quot;has the consumer freed any slots?&quot;), the latest value of &lt;code&gt;head&lt;&#x2F;code&gt; isn&#x27;t in the producer&#x27;s cache, and it isn&#x27;t even in RAM yet - it&#x27;s sitting dirty in the consumer core&#x27;s private cache.&lt;&#x2F;p&gt;
&lt;p&gt;Here&#x27;s the sequence the hardware runs when the producer issues that &lt;code&gt;Acquire&lt;&#x2F;code&gt; load:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;The producer&#x27;s core looks in its own L1&#x2F;L2 - it&#x27;s a &lt;strong&gt;miss&lt;&#x2F;strong&gt; (either it doesn&#x27;t have the line, or it has a stale copy that coherence has already invalidated).&lt;&#x2F;li&gt;
&lt;li&gt;The coherence protocol broadcasts a request for that cache line across the interconnect (on Intel, a ring&#x2F;mesh bus, across sockets).&lt;&#x2F;li&gt;
&lt;li&gt;The consumer&#x27;s core, which holds the line in Modified state, has to respond and ship the line over - a cache-to-cache transfer.&lt;&#x2F;li&gt;
&lt;li&gt;The line&#x27;s coherence state transitions (the consumer&#x27;s copy typically drops to Shared or Invalid), and the producer finally gets the value.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;That&#x27;s the &lt;strong&gt;coherence miss&lt;&#x2F;strong&gt; .. and the following diagram is an illustration of this ..&lt;&#x2F;p&gt;
&lt;pre class=&quot;mermaid&quot;&gt;
  
  sequenceDiagram
    participant P as Producer core
    participant IC as Interconnect
    participant C as Consumer core
    Note over C: holds cache line in Modified (dirty)
    P-&gt;&gt;P: Acquire load of head — L1&#x2F;L2 miss
    P-&gt;&gt;IC: request line (read for ownership)
    IC-&gt;&gt;C: snoop
    C-&gt;&gt;C: line is dirty (Modified)
    C--&gt;&gt;IC: cache-to-cache transfer
    Note over C: line drops M → Shared&#x2F;Invalid
    IC--&gt;&gt;P: deliver line
    Note over P: producer gets fresh head value
&lt;&#x2F;pre&gt;
&lt;p&gt;That whole round trip - request out, snoop, cache-to-cache transfer back - is what costs tens to hundreds of cycles:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Same core complex &#x2F; shared L3: ~tens of cycles.&lt;&#x2F;li&gt;
&lt;li&gt;Different core, same socket: ~40-80 cycles (~40 ns).&lt;&#x2F;li&gt;
&lt;li&gt;Across sockets (NUMA): can be well into the hundreds.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Do that once per item and your throughput is capped at roughly &lt;code&gt;1 &#x2F; cross_core_latency&lt;&#x2F;code&gt; items per second, no matter how fast the rest of your code is.&lt;&#x2F;p&gt;
&lt;p&gt;Note that the &lt;code&gt;Acquire&lt;&#x2F;code&gt; ordering isn&#x27;t itself what causes the coherence miss - the miss is about where the data lives. But &lt;code&gt;Acquire&lt;&#x2F;code&gt; is the reason you&#x27;re reading the real shared cursor at all (to get a correct, synchronized view of the producer&#x27;s published writes), rather than a stale private copy.&lt;&#x2F;p&gt;
&lt;p&gt;There are exactly two ways to drive that cost down, and this post takes a look at each of them:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Avoid the read&lt;&#x2F;strong&gt; - keep a private, conservative cache of the other core&#x27;s cursor and consult &lt;em&gt;that&lt;&#x2F;em&gt; on the fast path, paying the cross-core load only when the cache says you&#x27;re stuck. That&#x27;s Part 1 below and the biggest trick it employs is that you never try to keep &lt;code&gt;cached_head&lt;&#x2F;code&gt; fresh. You let it be stale on purpose and rely on the fact that a stale value is safe.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Amortize the read&lt;&#x2F;strong&gt; - when you must synchronize, do it once for a whole &lt;em&gt;batch&lt;&#x2F;em&gt; of items instead of once per item. (Part 2)&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Both rest directly on machinery from the earlier posts: the cache is the single-writer &lt;code&gt;UnsafeCell&lt;&#x2F;code&gt; Part 2 introduced, and the batch publish is the Release&#x2F;Acquire protocol Part 2 spent its first half on.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;part-1-cursor-caching-avoid-the-cross-core-read&quot;&gt;Part 1 - Cursor caching: avoid the cross-core read&lt;&#x2F;h2&gt;
&lt;p&gt;Most of the time, a core doesn&#x27;t actually &lt;em&gt;need&lt;&#x2F;em&gt; a fresh view of the other core&#x27;s cursor. A producer checking &quot;is there room for &lt;code&gt;n&lt;&#x2F;code&gt; items?&quot; only needs to know that &lt;code&gt;head&lt;&#x2F;code&gt; has advanced &lt;em&gt;far enough&lt;&#x2F;em&gt; - and if it had plenty of room last time it looked, it very likely still does. So instead of reading the real &lt;code&gt;head&lt;&#x2F;code&gt; every time, the producer keeps a &lt;strong&gt;single-writer cache&lt;&#x2F;strong&gt; of it, owned and written by the producer alone, and reads the real cursor only when the cache says it&#x27;s out of space. See the next section for why it&#x27;s perfectly safe and correct to read from the stale cache.&lt;&#x2F;p&gt;
&lt;p&gt;That cache is the &lt;code&gt;cached_head&lt;&#x2F;code&gt; field from Parts 1 and 2 - a bare &lt;code&gt;UnsafeCell&amp;lt;u64&amp;gt;&lt;&#x2F;code&gt; rather than an atomic, precisely because exactly one core ever writes it.&lt;&#x2F;p&gt;
&lt;p&gt;Concretely, this shows up in &lt;code&gt;reserve&lt;&#x2F;code&gt; function below - the producer&#x27;s entry point for claiming space in the ring. A caller asks for room for &lt;code&gt;n&lt;&#x2F;code&gt; items; &lt;code&gt;reserve&lt;&#x2F;code&gt; returns a &lt;code&gt;Reservation&lt;&#x2F;code&gt; it can write into if the ring has space, or &lt;code&gt;None&lt;&#x2F;code&gt; if it&#x27;s full.  The whole point of the method is to answer &quot;is there room for &lt;code&gt;n&lt;&#x2F;code&gt;?&quot; as cheaply as possible, and that question is exactly where the cached cursor does the trick: the fast path answers it from the producer&#x27;s own cache with no cross-core traffic, falling back to the real &lt;code&gt;Acquire&lt;&#x2F;code&gt; load only when the cache reports the ring full:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;pub fn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; reserve&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-storage&quot;&gt;&amp;amp;mut&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language z-self&quot;&gt; self&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-variable&quot;&gt;, n&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; usize&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Option&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;Reservation&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;_&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; T&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; A&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;gt;&amp;gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;    let&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; tail&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language z-self&quot;&gt; self&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;tail&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;load&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-namespace&quot;&gt;Ordering&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;Relaxed&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;);&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;   &#x2F;&#x2F; our own cursor: cheap&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;    &#x2F;&#x2F; fast path: consult the producer-owned cache. No cross-core traffic.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;    &#x2F;&#x2F; safety: cached_head is written only by the (unique) producer thread.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;    let&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; cached_head&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; unsafe&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; *&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language z-self&quot;&gt;self&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;cached_head&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;get&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;() };&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;    let&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; space&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language z-self&quot;&gt; self&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;capacity&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;()&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;        .&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;saturating_sub&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-variable&quot;&gt;(tail&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;wrapping_sub&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-variable&quot;&gt;(cached_head)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; as&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; usize&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    if&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; space&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; &amp;gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt; n {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        return&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Some&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language z-self&quot;&gt;self&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;make_reservation&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-variable&quot;&gt;(tail, n));&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;    &#x2F;&#x2F; slow path: the cache says we&amp;#39;re full. *Now* pay for the cross-core&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;    &#x2F;&#x2F; Acquire load, and refresh the cache for next time.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;    let&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; head&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language z-self&quot;&gt; self&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;head&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;load&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-namespace&quot;&gt;Ordering&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;Acquire&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;);&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;   &#x2F;&#x2F; potential coherence miss&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;    &#x2F;&#x2F; safety: same single-writer guarantee.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    unsafe&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; *&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language z-self&quot;&gt;self&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;cached_head&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;get&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;()&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt; head; }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;    let&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; space&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language z-self&quot;&gt; self&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;capacity&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;()&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;        .&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;saturating_sub&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-variable&quot;&gt;(tail&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;wrapping_sub&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-variable&quot;&gt;(head)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; as&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; usize&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-variable&quot;&gt;    (space&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; &amp;gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt; n)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;then&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;||&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language z-self&quot;&gt; self&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;make_reservation&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-variable&quot;&gt;(tail, n))&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The consumer side is the mirror image: it caches &lt;code&gt;tail&lt;&#x2F;code&gt; in a &lt;code&gt;cached_tail&lt;&#x2F;code&gt; &lt;code&gt;UnsafeCell&lt;&#x2F;code&gt; and reads the real &lt;code&gt;tail&lt;&#x2F;code&gt; with &lt;code&gt;Acquire&lt;&#x2F;code&gt; only when its cache reports the ring empty.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;why-a-stale-cache-is-always-safe&quot;&gt;Why a stale cache is always safe&lt;&#x2F;h3&gt;
&lt;p&gt;The subtle part is that the cache can be &lt;em&gt;wrong&lt;&#x2F;em&gt; and the code is still correct. The key property: &lt;strong&gt;a stale view of &lt;code&gt;head&lt;&#x2F;code&gt; can only ever under-report available space, never over-report it.&lt;&#x2F;strong&gt; &lt;code&gt;head&lt;&#x2F;code&gt; moves in exactly one direction (the consumer only ever frees slots), so a cached &lt;code&gt;head&lt;&#x2F;code&gt; that lags behind reality makes the producer think it has &lt;em&gt;less&lt;&#x2F;em&gt; room than it really does. The worst that happens is an unnecessary trip to the slow path - never a write into a slot that overwrites existing data and that which the consumer hasn&#x27;t actually released. Conservative caches are safe caches.&lt;&#x2F;p&gt;
&lt;p&gt;The control flow makes the safety visible: a stale cache can only ever divert you &lt;em&gt;down&lt;&#x2F;em&gt; into the slow path, and the slow path re-checks against the real &lt;code&gt;head&lt;&#x2F;code&gt; before it ever hands out a reservation. There is no edge that turns a stale cache into an over-report.&lt;&#x2F;p&gt;
&lt;pre class=&quot;mermaid&quot;&gt;
  
  flowchart TD
    A[&quot;reserve(n): load own tail (Relaxed)&quot;] --&gt; B{&quot;fast path: space &gt;= n?&lt;br&#x2F;&gt;(computed from cached_head)&quot;}
    B -- yes --&gt; C[&quot;make_reservation — no cross-core read&quot;]
    B -- &quot;no (cache says full)&quot; --&gt; D[&quot;slow path: Acquire load of real head&lt;br&#x2F;&gt;+ refresh cached_head&quot;]
    D --&gt; E{&quot;real space &gt;= n?&quot;}
    E -- &quot;yes (cache was stale &amp;amp; pessimistic)&quot; --&gt; F[&quot;make_reservation&quot;]
    E -- no --&gt; G[&quot;genuinely full → None&quot;]
&lt;&#x2F;pre&gt;
&lt;p&gt;And under steady-state operation the cache is stale &lt;em&gt;most of the time&lt;&#x2F;em&gt;, which is exactly when it pays off. If the consumer is keeping up, the producer&#x27;s cached &lt;code&gt;head&lt;&#x2F;code&gt; lags by a bounded amount and the fast path keeps succeeding; the cross-core &lt;code&gt;Acquire&lt;&#x2F;code&gt; fires only at the moments the producer genuinely catches up to the consumer and has to re-check. The cache turns a per-call coherence miss into an occasional one.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;cache-asymmetrically-not-every-cross-core-read-wants-a-cache&quot;&gt;Cache asymmetrically - not every cross-core read wants a cache&lt;&#x2F;h3&gt;
&lt;p&gt;Here&#x27;s the part that&#x27;s easy to overdo. Having seen the win, the instinct is to cache &lt;em&gt;every&lt;&#x2F;em&gt; cross-core read. That&#x27;s wrong, and the reason is instructive.&lt;&#x2F;p&gt;
&lt;p&gt;Consider two different consumer-side operations:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;A &lt;strong&gt;&lt;code&gt;readable()&lt;&#x2F;code&gt; poll&lt;&#x2F;strong&gt; - &quot;is there any data?&quot; - is often called in a tight loop (e.g. an async task spinning until woken). It &lt;em&gt;should&lt;&#x2F;em&gt; use &lt;code&gt;cached_tail&lt;&#x2F;code&gt;: on an empty ring, each poll costs one &lt;code&gt;UnsafeCell&lt;&#x2F;code&gt; read, and the cross-core &lt;code&gt;Acquire&lt;&#x2F;code&gt; fires only when the cache is stale. The cross-core read happens &lt;em&gt;per check&lt;&#x2F;em&gt;, so caching saves a coherence miss on nearly every call.&lt;&#x2F;p&gt;
&lt;p&gt;Now you might ask &quot;can the &lt;code&gt;cached_tail&lt;&#x2F;code&gt; ever give me a wrong answer ?&quot;. Never, for the same reason the producer&#x27;s stale &lt;code&gt;cached_head&lt;&#x2F;code&gt; is safe as explained above. The consumer caches &lt;code&gt;tail&lt;&#x2F;code&gt; (the producer&#x27;s cursor). &lt;code&gt;tail&lt;&#x2F;code&gt; moves in exactly one direction - the producer only ever advances it as it publishes more data - so a stale &lt;code&gt;cached_tail&lt;&#x2F;code&gt; always lags behind the real &lt;code&gt;tail&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;cached_tail  &amp;lt;=  real_tail        (always)&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;A &lt;strong&gt;batch consume&lt;&#x2F;strong&gt; (the subject of Part 2 below) reads &lt;code&gt;tail&lt;&#x2F;code&gt; &lt;em&gt;directly&lt;&#x2F;em&gt; with &lt;code&gt;Acquire&lt;&#x2F;code&gt; and skips the cache entirely. Why? Because that single load already determines how many items the &lt;em&gt;entire batch&lt;&#x2F;em&gt; can consume. Its cost is O(1) per batch regardless of batch size, so a cache would save ~20 ns once per batch while adding a branch to every call. Pure overhead.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The rule: &lt;strong&gt;cache when the cross-core read happens &lt;em&gt;per check&lt;&#x2F;em&gt;; don&#x27;t when it already happens &lt;em&gt;per batch&lt;&#x2F;em&gt;.&lt;&#x2F;strong&gt; If an operation already amortizes synchronization across N items, an extra cache layer is decoration - it adds a branch and a maintenance burden to save a cost that&#x27;s already negligible. Which is the perfect segue, because amortization is the other half of this post.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;part-2-batch-amortization-pay-the-atomic-once-per-n-items&quot;&gt;Part 2 - Batch amortization: pay the atomic once per N items&lt;&#x2F;h2&gt;
&lt;p&gt;Caching avoids the cross-core &lt;em&gt;read&lt;&#x2F;em&gt;. Batching attacks the problem from the other direction: when you genuinely have to touch the shared cursors, touch them &lt;strong&gt;once for a whole run of items&lt;&#x2F;strong&gt; instead of once each.&lt;&#x2F;p&gt;
&lt;p&gt;The model here is the LMAX Disruptor&#x27;s: the consumer reads &lt;code&gt;tail&lt;&#x2F;code&gt; once with &lt;code&gt;Acquire&lt;&#x2F;code&gt;, processes everything up to it with no atomics in the loop, then writes &lt;code&gt;head&lt;&#x2F;code&gt; once with &lt;code&gt;Release&lt;&#x2F;code&gt; to publish all of that progress at once.&lt;&#x2F;p&gt;
&lt;p&gt;The interesting part isn&#x27;t the atomic count - it&#x27;s doing this &lt;em&gt;panic-safely&lt;&#x2F;em&gt;. If the user&#x27;s per-item handler unwinds midway through the batch, we must still publish the items we already moved out, so that a later consume (or the ring&#x27;s own destructor) doesn&#x27;t read or free them a second time. A &lt;code&gt;Drop&lt;&#x2F;code&gt; guard handles that:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;struct&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; ConsumeGuard&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;a&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; T&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; A&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; BufferAllocator&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    ring&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:    &amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;a Ring&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;T&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; A&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    current&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; u64&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;         &#x2F;&#x2F; advanced after each successful handler call&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;impl&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;T&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; A&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; BufferAllocator&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Drop&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; for&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; ConsumeGuard&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;_&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; T&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; A&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    fn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; drop&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-storage&quot;&gt;&amp;amp;mut&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language z-self&quot;&gt; self&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;        &#x2F;&#x2F; Publish whatever progress we made - including on a panic unwind.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-language z-self&quot;&gt;        self&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;ring&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;head&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;store&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language z-self&quot;&gt;self&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function&quot;&gt;current&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-namespace&quot;&gt; Ordering&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;Release&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;pub fn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; consume_batch&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;F&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language z-self&quot;&gt;self&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage&quot;&gt; mut&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; handler&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; F&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; usize&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;where&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;    F&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; FnMut&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;T&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;    let&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; head&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language z-self&quot;&gt; self&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;head&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;load&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-namespace&quot;&gt;Ordering&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;Relaxed&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;);&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;   &#x2F;&#x2F; our own cursor: cheap&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;    let&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; tail&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language z-self&quot;&gt; self&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;tail&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;load&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-namespace&quot;&gt;Ordering&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;Acquire&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;);&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;   &#x2F;&#x2F; ONE cross-core load&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;    let&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; avail&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; tail&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;wrapping_sub&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-variable&quot;&gt;(head)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; as&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; usize&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    if&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; avail&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; ==&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        return&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust z-storage&quot;&gt;    let mut&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; guard&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; ConsumeGuard&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-variable&quot;&gt; { ring&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language z-self&quot;&gt; self&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-variable&quot;&gt;, current&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt; head };&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    while&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; guard&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;current &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;!=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt; tail {&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;                   &#x2F;&#x2F; NO atomics in this loop&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;        let&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; idx&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-variable&quot;&gt; (guard&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;current &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;as&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; usize&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language z-self&quot;&gt; self&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;mask&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;();&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;        &#x2F;&#x2F; SAFETY: head &amp;lt;= idx &amp;lt; tail, and the producer published this slot&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;        &#x2F;&#x2F; via the Acquire load above, so it is initialized.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;        let&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; item&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; unsafe&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;            let&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; buf&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; = &amp;amp;*&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language z-self&quot;&gt;self&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;buffer&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;get&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;();&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt;            buf[idx]&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;assume_init_read&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;()&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;        };&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;        &#x2F;&#x2F; Advance the cursor *before* invoking the handler - see below.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;        guard&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;current &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; guard&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;current&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;wrapping_add&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;1&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;        handler&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-variable&quot;&gt;(item);&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;                              &#x2F;&#x2F; by value; may panic&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;    &#x2F;&#x2F; Normal exit: the guard&amp;#39;s Drop publishes the final `head` via Release.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    avail&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The whole-batch path now touches atomics &lt;strong&gt;three times total&lt;&#x2F;strong&gt; - a local &lt;code&gt;Relaxed&lt;&#x2F;code&gt; load of &lt;code&gt;head&lt;&#x2F;code&gt; (hot in our own cache), one cross-core &lt;code&gt;Acquire&lt;&#x2F;code&gt; load of &lt;code&gt;tail&lt;&#x2F;code&gt; (the only potentially expensive one), and one &lt;code&gt;Release&lt;&#x2F;code&gt; store of &lt;code&gt;head&lt;&#x2F;code&gt; on the way out (local to the consumer&#x27;s cache line) - instead of three times &lt;em&gt;per item&lt;&#x2F;em&gt;. For a batch of any meaningful size the per-item synchronization cost vanishes into the noise. In one benchmarked implementation, batched consumption brought the amortized per-item cost down to ~2 ns, versus ~6 ns when every item paid its own atomic toll; your exact numbers will depend on item size, batch size, and target.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;why-the-cursor-advances-before-the-handler-runs&quot;&gt;Why the cursor advances &lt;em&gt;before&lt;&#x2F;em&gt; the handler runs&lt;&#x2F;h3&gt;
&lt;p&gt;This ordering looks backwards on first read, and getting it wrong is a real memory-safety bug, not a style nit.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;assume_init_read()&lt;&#x2F;code&gt; &lt;strong&gt;moves&lt;&#x2F;strong&gt; the value out of the slot - afterward the slot is logically uninitialized. Suppose you advanced the cursor &lt;em&gt;after&lt;&#x2F;em&gt; the handler instead, and the handler panicked. The slot now holds a moved-out value, but &lt;code&gt;head&lt;&#x2F;code&gt; still points &lt;em&gt;at&lt;&#x2F;em&gt; it. When the guard&#x27;s &lt;code&gt;Drop&lt;&#x2F;code&gt; publishes &lt;code&gt;head&lt;&#x2F;code&gt; - or when the ring is later dropped - that slot gets &lt;code&gt;assume_init_read&lt;&#x2F;code&gt; called on it &lt;em&gt;again&lt;&#x2F;em&gt;: a use-after-move, and for non-&lt;code&gt;Copy&lt;&#x2F;code&gt; types a double-drop.&lt;&#x2F;p&gt;
&lt;p&gt;Advancing &lt;em&gt;before&lt;&#x2F;em&gt; the handler runs keeps the published &lt;code&gt;head&lt;&#x2F;code&gt; honest: it always means &quot;every slot below here has already left our ownership.&quot; Whatever the handler does, the guard publishes a cursor that never points at a slot we already emptied.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;the-idle-poll-case-is-cheap-by-construction&quot;&gt;The idle-poll case is cheap by construction&lt;&#x2F;h3&gt;
&lt;p&gt;A consumer polling an empty ring in a tight loop is the common case, and this shape handles it for nearly free: when &lt;code&gt;avail == 0&lt;&#x2F;code&gt; the function returns before constructing the guard, so &lt;em&gt;no&lt;&#x2F;em&gt; &lt;code&gt;Release&lt;&#x2F;code&gt; store is issued - the call costs exactly two atomic loads (near zero on a cache hit, ~40 ns on a cross-core miss).  An idle consumer burns minimal CPU without any special-casing.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;don-t-let-observability-sneak-back-into-the-loop&quot;&gt;Don&#x27;t let observability sneak back into the loop&lt;&#x2F;h3&gt;
&lt;p&gt;One easy way to throw the entire optimization away: increment a metric counter inside the per-item loop. A &lt;code&gt;fetch_add&lt;&#x2F;code&gt; is ~10 ns on x86 (&lt;code&gt;lock xadd&lt;&#x2F;code&gt;). Call it 1,000 times per batch and you&#x27;ve spent 10 µs per batch re-introducing exactly the per-item atomic cost you just eliminated. Hoist counters out and update them once per batch:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;if&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language z-self&quot;&gt; self&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;config&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;enable_metrics {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-language z-self&quot;&gt;    self&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;metrics&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;add_messages_received&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-variable&quot;&gt;(count&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; as&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; u64&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;);&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;   &#x2F;&#x2F; ONE atomic&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-language z-self&quot;&gt;    self&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;metrics&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;add_batches_received&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;1&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The principle is general: &lt;strong&gt;anything you do per item - synchronization, metrics, logging - must be cheap, or it has to move out of the loop.&lt;&#x2F;strong&gt; Batching the data path while leaving a &lt;code&gt;lock xadd&lt;&#x2F;code&gt; in the loop body is a self-inflicted wound.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;the-trade-off-and-how-to-bound-it&quot;&gt;The trade-off, and how to bound it&lt;&#x2F;h3&gt;
&lt;p&gt;Batching trades a little latency for a lot of throughput: the &lt;em&gt;first&lt;&#x2F;em&gt; item in a batch can&#x27;t be delivered until the batch has formed. Where tail latency matters more than peak throughput, expose a bounded variant - &lt;code&gt;consume_up_to(max, handler)&lt;&#x2F;code&gt; - that caps how many items a single call will accumulate, so no item waits behind an unboundedly large batch.&lt;&#x2F;p&gt;
&lt;p&gt;The same amortize-the-expensive-operation move shows up all over systems code:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Disk I&#x2F;O:&lt;&#x2F;strong&gt; group commit in write-ahead logs - one &lt;code&gt;fsync&lt;&#x2F;code&gt; per batch of records instead of one per record.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Network I&#x2F;O:&lt;&#x2F;strong&gt; TCP coalescing; &lt;code&gt;io_uring&lt;&#x2F;code&gt; submission queues that batch many operations behind a single tail update.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Allocators:&lt;&#x2F;strong&gt; returning N freed blocks to a central free-list in one operation instead of N.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;the-two-halves-are-the-same-instinct&quot;&gt;The two halves are the same instinct&lt;&#x2F;h2&gt;
&lt;p&gt;Caching and batching look like different techniques, but they&#x27;re the same idea aimed at the same cost. The cross-core atomic is expensive, so: &lt;strong&gt;avoid it when a conservative local snapshot will do, and amortize it across many items when you can&#x27;t avoid it.&lt;&#x2F;strong&gt; Caching removes the read on the fast path; batching removes it from the per-item path. Used together, a busy producer&#x2F;consumer pair touches shared memory a tiny fraction as often as a naive one-atomic-per-item design - which is what turns the careful layout of Part 1 and the correct ordering of Part 2 into actual throughput.&lt;&#x2F;p&gt;
&lt;p&gt;There&#x27;s a third move hiding in the &lt;code&gt;reserve()&lt;&#x2F;code&gt; example above: the producer wrote &lt;em&gt;directly&lt;&#x2F;em&gt; into the ring&#x27;s slots via the reservation, with no intermediate copy.  That zero-copy &lt;code&gt;reserve&lt;&#x2F;code&gt;&#x2F;&lt;code&gt;commit&lt;&#x2F;code&gt; protocol - and the fast-path&#x2F;slow-path discipline that the caching example is one instance of - is where Part 4 goes.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>The Cross-Core Contract: Memory Ordering and Single-Writer State in Lock-Free Rust</title>
          <pubDate>Mon, 06 Jul 2026 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://debasishg.github.io/blog/part2-cross-core-contract-memory-ordering-and-single-writer-state/</link>
          <guid>https://debasishg.github.io/blog/part2-cross-core-contract-memory-ordering-and-single-writer-state/</guid>
          <description xml:base="https://debasishg.github.io/blog/part2-cross-core-contract-memory-ordering-and-single-writer-state/">&lt;h1 id=&quot;the-cross-core-contract-memory-ordering-and-single-writer-state-in-lock-free-rust&quot;&gt;The Cross-Core Contract: Memory Ordering and Single-Writer State in Lock-Free Rust&lt;&#x2F;h1&gt;
&lt;p&gt;Part 2 of &lt;strong&gt;Low-Level Systems Design in Rust&lt;&#x2F;strong&gt; - a series on writing high-throughput, low-latency systems code, using a single-producer &#x2F; single-consumer (SPSC) ring buffer as the running example.&lt;&#x2F;p&gt;
&lt;p&gt;Part 1, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;debasishg.github.io&#x2F;blog&#x2F;part1-cache-conscious-data-layout-in-rust&#x2F;&quot;&gt;&lt;em&gt;Cache-Conscious Data Layout&lt;&#x2F;em&gt;&lt;&#x2F;a&gt;, decided where the shared fields of a concurrent structure live in memory.&lt;&#x2F;p&gt;
&lt;p&gt;This one covers how two cores read and write those fields - correctly, and as cheaply as the hardware allows. As before, the running example is a single-producer &#x2F; single-consumer (SPSC) ring buffer, but the ideas apply to any lock-free queue, counter, or reclamation scheme.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;recap-and-the-question-this-post-answers&quot;&gt;Recap, and the question this post answers&lt;&#x2F;h2&gt;
&lt;p&gt;In the first post we set the foundation with a ring buffer so that the two cursors, &lt;code&gt;tail&lt;&#x2F;code&gt; (where the producer writes next) and &lt;code&gt;head&lt;&#x2F;code&gt; (where the consumer reads next), sit on separate cache lines. This removes false sharing between unrelated hot fields: producer-owned updates no longer invalidate the consumer’s private cursor state, and consumer-owned updates no longer invalidate the producer’s private cursor state. The published cursors are still shared synchronization points; they are simply isolated from unrelated traffic.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;#[&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-attribute z-rust&quot;&gt;repr&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;C&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;pub&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt; struct&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Ring&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;T&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; A&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; BufferAllocator&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; HeapAllocator&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;    &#x2F;&#x2F; producer hot &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    tail&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;        CacheAligned&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;AtomicU64&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    cached_head&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; CacheAligned&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;UnsafeCell&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;u64&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;gt;&amp;gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;    &#x2F;&#x2F; consumer hot &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    head&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;        CacheAligned&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;AtomicU64&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    cached_tail&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; CacheAligned&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;UnsafeCell&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;u64&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;gt;&amp;gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;    &#x2F;&#x2F; cold&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;    &#x2F;&#x2F; closed, metrics, config, buffer ...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;With the above solution we solved &quot;placement&quot;, a static problem. In this post we talk about the &lt;strong&gt;dynamic problem&lt;&#x2F;strong&gt;, which deals with the runtime. When the producer publishes a new &lt;code&gt;tail&lt;&#x2F;code&gt;, what guarantees that the
consumer sees the data the producer wrote before it?&lt;&#x2F;p&gt;
&lt;p&gt;One solution could be to make the fields &lt;code&gt;AtomicU64&lt;&#x2F;code&gt;. But it has a cost that we can try to avoid. Can we make it cheaper ?&lt;&#x2F;p&gt;
&lt;p&gt;Note that two of the four hot fields above are &lt;code&gt;AtomicU64&lt;&#x2F;code&gt; and two are a bare &lt;code&gt;UnsafeCell&amp;lt;u64&amp;gt;&lt;&#x2F;code&gt; - that asymmetry is the subject of Part 2.&lt;&#x2F;p&gt;
&lt;p&gt;We explore 2 ideas to answer the question, and both of them distill down to the same underlying theme:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Minimum-sufficient memory ordering - every atomic operation is a contract with the hardware, pick the cheapest contract that&#x27;s still correct.&lt;&#x2F;li&gt;
&lt;li&gt;Single-writer invariants - when a field has exactly one writer, the cheapest correct contract is no atomic at all.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;part-1-memory-ordering-the-minimum-sufficient-rule&quot;&gt;Part 1 - Memory ordering: the minimum-sufficient rule&lt;&#x2F;h2&gt;
&lt;p&gt;The most common mistake with atomics is beig ultra defensive (or pessimistic) and treating the ordering parameter as a safety dial - &quot;when in doubt, turn it up to &lt;code&gt;SeqCst&lt;&#x2F;code&gt;.&quot; This can give the correct emergent behavior of your code, but along with correctness, it imposes a &quot;tax&quot; as well, which you may not need to pay.&lt;&#x2F;p&gt;
&lt;p&gt;Treat memory ordering as a contract with the hardware about which reorderings are forbidden.&lt;&#x2F;p&gt;
&lt;p&gt;Stronger orderings emit more fences and slower instructions on weakly-ordered ISAs (ARM, RISC-V), and forbid more compiler reordering on &lt;em&gt;all&lt;&#x2F;em&gt; of them.&lt;&#x2F;p&gt;
&lt;p&gt;One general rule of thumb that you can follow is to use the &lt;em&gt;minimum&lt;&#x2F;em&gt; ordering that establishes the happens-before relationship your algorithm actually requires.&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Pattern&lt;&#x2F;th&gt;&lt;th&gt;Correct ordering&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Statistical counters (no control flow depends on value)&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;Relaxed&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;A same-thread atomic used like a regular variable&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;Relaxed&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Publish data to another thread&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;Release&lt;&#x2F;code&gt; on the store&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Read data published by another thread&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;Acquire&lt;&#x2F;code&gt; on the load&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Mutually exclusive global ordering across all threads&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;SeqCst&lt;&#x2F;code&gt; (rare)&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;h3 id=&quot;publication-the-one-pattern-that-carries-the-whole-protocol&quot;&gt;Publication: the one pattern that carries the whole protocol&lt;&#x2F;h3&gt;
&lt;p&gt;The producer writes its data into the buffer, then publishes the new &lt;code&gt;tail&lt;&#x2F;code&gt; with &lt;code&gt;Release&lt;&#x2F;code&gt;. The consumer observes &lt;code&gt;tail&lt;&#x2F;code&gt; with &lt;code&gt;Acquire&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;&#x2F;&#x2F; Producer - after writing buffer slots:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-language z-self&quot;&gt;self&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;tail&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;store&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-variable&quot;&gt;(new_tail,&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-namespace&quot;&gt; Ordering&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;Release&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;);&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;   &#x2F;&#x2F; publish&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;&#x2F;&#x2F; Consumer:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;let&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; tail&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language z-self&quot;&gt; self&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;tail&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;load&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-namespace&quot;&gt;Ordering&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;Acquire&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;);&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;   &#x2F;&#x2F; observe&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The above pattern enforces the exact guarantee that everything the producer wrote &lt;strong&gt;before&lt;&#x2F;strong&gt; the &lt;code&gt;Release&lt;&#x2F;code&gt; store becomes visible to the consumer &lt;strong&gt;after&lt;&#x2F;strong&gt; the consumer performs an &lt;code&gt;Acquire&lt;&#x2F;code&gt; load that observes that published value. We need no global ordering, and hence no &lt;code&gt;SeqCst&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Now you can draw the chain explicitly - the correctness of any Release&#x2F;Acquire protocol is an audit you can do by pointing at four edges:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;producer.write(buffer[idx])&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    | sequenced-before&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    v&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;producer.tail.store(new_tail, Release)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    | synchronizes-with&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    v&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;consumer.tail.load(Acquire)  - observes new_tail&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    | sequenced-before&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    v&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;consumer.read(buffer[idx])   - sees the producer&amp;#39;s write&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The &lt;code&gt;Release&lt;&#x2F;code&gt; store is the &lt;strong&gt;publication edge&lt;&#x2F;strong&gt;: it&#x27;s the single point that makes everything sequenced before it on the producer become visible to any thread that observes the new value via an &lt;code&gt;Acquire&lt;&#x2F;code&gt; load.&lt;&#x2F;p&gt;
&lt;p&gt;&quot;Publication edge&quot; is a &lt;em&gt;semantic&lt;&#x2F;em&gt; role, not a claim about the emitted instruction - on some targets it compiles to an ordinary store (more on that below).&lt;&#x2F;p&gt;
&lt;h3 id=&quot;statistics-relaxed-but-what-relaxed-really-buys-us&quot;&gt;Statistics: &lt;code&gt;Relaxed&lt;&#x2F;code&gt;, but what &quot;relaxed&quot; really buys us&lt;&#x2F;h3&gt;
&lt;p&gt;A metrics counter doesn&#x27;t gate any control flow - nobody branches on the exact running total - so it preserves atomicity for the counter itself, but establishes no happens-before relationship for surrounding memory operations.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-language z-self&quot;&gt;self&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;messages_sent&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;fetch_add&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-variable&quot;&gt;(n,&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-namespace&quot;&gt; Ordering&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;Relaxed&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;Relaxed&lt;&#x2F;code&gt; drops the inter-thread ordering constraints: neither the compiler nor the hardware has to establish a happens-before relationship with surrounding operations on other locations. What &lt;code&gt;Relaxed&lt;&#x2F;code&gt; does NOT drop is the atomicity of the read-modify-write itself. On x86 that &lt;code&gt;fetch_add&lt;&#x2F;code&gt; is still a &lt;code&gt;lock xadd&lt;&#x2F;code&gt; (~10 ns); on AArch64 it&#x27;s an &lt;code&gt;ldxr&lt;&#x2F;code&gt;&#x2F;&lt;code&gt;stxr&lt;&#x2F;code&gt; retry loop.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;Relaxed&lt;&#x2F;code&gt; is not a free local-variable increment. If per-call atomic cost shows up on a genuinely hot path, the fix isn&#x27;t a weaker ordering (there isn&#x27;t one) - it&#x27;s to stop touching the shared atomic every call: accumulate in a thread-local and flush periodically.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;the-cost-is-target-dependent-and-that-s-the-whole-point&quot;&gt;The cost is target-dependent - and that&#x27;s the whole point&lt;&#x2F;h3&gt;
&lt;p&gt;Here&#x27;s the subtlety that makes minimum-sufficient ordering matter in practice rather than in theory: the same source compiles to very different costs depending on the CPU&#x27;s memory model.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;On x86-64, plain acquire loads and release stores are usually nearly free.&lt;&#x2F;strong&gt; The x86-TSO model already provides the ordering those operations need, so &lt;code&gt;Release&lt;&#x2F;code&gt; stores and &lt;code&gt;Acquire&lt;&#x2F;code&gt; loads typically lower to ordinary &lt;code&gt;mov&lt;&#x2F;code&gt; instructions - the ordering annotations exist mostly to constrain the compiler. Do not over-generalize this to &quot;atomics are free on x86&quot;:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;atomic read-modify-writes (&lt;code&gt;fetch_add&lt;&#x2F;code&gt;, &lt;code&gt;compare_exchange&lt;&#x2F;code&gt;) still lower to &lt;code&gt;lock&lt;&#x2F;code&gt;-prefixed instructions regardless of ordering&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;SeqCst&lt;&#x2F;code&gt; stores lower to &lt;code&gt;xchg&lt;&#x2F;code&gt;, or &lt;code&gt;mov&lt;&#x2F;code&gt; + &lt;code&gt;mfence&lt;&#x2F;code&gt;; - target-specific lowering can change.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;On AArch64, ARMv7, and RISC-V the cost is real and visible.&lt;&#x2F;strong&gt; These are weakly ordered: the CPU reorders loads and stores freely unless told not to. &lt;code&gt;Release&lt;&#x2F;code&gt; typically selects &lt;code&gt;stlr&lt;&#x2F;code&gt; (store-release) on AArch64 or &lt;code&gt;dmb ish&lt;&#x2F;code&gt; + &lt;code&gt;str&lt;&#x2F;code&gt; on ARMv7; &lt;code&gt;Acquire&lt;&#x2F;code&gt; selects &lt;code&gt;ldar&lt;&#x2F;code&gt; or &lt;code&gt;ldr&lt;&#x2F;code&gt; + &lt;code&gt;dmb ish&lt;&#x2F;code&gt;. Each fence costs tens of cycles. &lt;code&gt;SeqCst&lt;&#x2F;code&gt; is worse - a full barrier on every operation.&lt;&#x2F;p&gt;
&lt;p&gt;This is why the rule isn&#x27;t a micro-optimization. Using &lt;code&gt;Relaxed&lt;&#x2F;code&gt; where it suffices and Release&#x2F;Acquire only at publication points keeps the fence count to the minimum the algorithm requires. That&#x27;s what makes the difference between source that&#x27;s fast on both an Intel server and a Graviton or Apple-silicon core, and source that&#x27;s correct but stalls the pipeline on half your fleet. On a weakly ordered machine, minimum-sufficient ordering is the line between an optimally working protocol and a stalled one.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;once-per-lifetime-is-not-a-reason-to-reach-for-seqcst&quot;&gt;&quot;Once per lifetime&quot; is not a reason to reach for &lt;code&gt;SeqCst&lt;&#x2F;code&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;In many related scenarios I have seen developers reaching out to a tempting rationalization: &quot;this atomic is only touched once, at startup, so &lt;code&gt;SeqCst&lt;&#x2F;code&gt; is fine - who cares about the cost?&quot; That reasons from the wrong premise. The cost isn&#x27;t the point; &lt;strong&gt;correctness intent&lt;&#x2F;strong&gt; is (sometimes we call this &quot;convenience over correctness&quot;). Pick the ordering the algorithm actually needs. If registering a new participant just needs a unique slot index followed by a separate &lt;code&gt;Release&lt;&#x2F;code&gt; publication, then &lt;code&gt;Relaxed&lt;&#x2F;code&gt; or &lt;code&gt;AcqRel&lt;&#x2F;code&gt; is the correct ordering, and &lt;code&gt;SeqCst&lt;&#x2F;code&gt; is noise that misleads the next reader into thinking a global total order matters here.&lt;&#x2F;p&gt;
&lt;p&gt;Reach for &lt;code&gt;SeqCst&lt;&#x2F;code&gt; only when the protocol genuinely requires a single global total order across unrelated atomics - and when it does, document which total order and why. &quot;Two threads must observe these two unrelated events in the same order&quot; is a justification. &quot;It&#x27;s only called once&quot; is not.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;documenting-the-protocol-clearly-can-be-a-good-idea&quot;&gt;Documenting the protocol clearly can be a good idea&lt;&#x2F;h3&gt;
&lt;p&gt;Release&#x2F;Acquire discipline is fragile precisely because the ordering arguments are non-local - the correctness of a store here depends on a load over there. So write the protocol down once, next to the type definition, as a numbered sequence:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Producer (write path):                 Consumer (read path):&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;1. Load  tail  Relaxed                 1. Load  head  Relaxed&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;2. Read  cached_head (UnsafeCell)      2. Read  cached_tail (UnsafeCell)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;3. If short: Load head  Acquire        3. If short: Load tail  Acquire&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;4. Write buffer slots                  4. Read  buffer slots&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;5. Store tail  Release  (publish)      5. Store head  Release  (publish)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now anyone editing step 4 can see that step 5 is the publication that makes their write visible, and that step 3 is the matching acquire on the other side. The two columns are mirror images - which is exactly what you&#x27;d expect of a symmetric producer&#x2F;consumer protocol, and a good sign you got it right.&lt;&#x2F;p&gt;
&lt;p&gt;Finally: pair every non-trivial ordering decision with a model-checked concurrency test (e.g. a Loom test that exhaustively explores interleavings).  Ordering bugs don&#x27;t reproduce under casual testing - they need a tool that enumerates the reorderings the hardware is allowed to perform.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;part-2-single-writer-invariants-and-unsafecell&quot;&gt;Part 2 - Single-writer invariants and &lt;code&gt;UnsafeCell&lt;&#x2F;code&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Part 1 was about choosing the cheapest possible atomic. Part 2 is about noticing when you don&#x27;t need an atomic at all.&lt;&#x2F;p&gt;
&lt;p&gt;Even &lt;code&gt;Relaxed&lt;&#x2F;code&gt; atomics aren&#x27;t free in the way people assume. Beyond the instruction cost, an &lt;code&gt;Atomic*&lt;&#x2F;code&gt; access is opaque to the optimizer: the compiler may not reuse a loaded value across an opaque function call, nor merge adjacent loads of the same atomic, nor hoist one out of a loop. Those are real optimizations you forfeit. When a memory location has exactly one writer, you can reclaim all of them.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;The pattern:&lt;&#x2F;strong&gt; if you can prove - and document - that a location is written by exactly one thread, store it in &lt;code&gt;UnsafeCell&amp;lt;T&amp;gt;&lt;&#x2F;code&gt; instead of &lt;code&gt;Atomic&amp;lt;T&amp;gt;&lt;&#x2F;code&gt;. Reads from the writer thread are plain reads; any reads from other threads need a separate synchronization channel (in the ring, that channel is the Release&#x2F;Acquire protocol on the cursors from Part 1).&lt;&#x2F;p&gt;
&lt;p&gt;Recall the two cursor caches from the struct at the top. Each core keeps a private snapshot of the other core&#x27;s cursor, to avoid a cross-core read on the fast path. The producer&#x27;s snapshot of &lt;code&gt;head&lt;&#x2F;code&gt; has exactly one writer - the producer - so it isn&#x27;t an atomic:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;cached_head&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; CacheAligned&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;UnsafeCell&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;u64&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;gt;&amp;gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;&#x2F;&#x2F; SAFETY: cached_head is only ever written by the (single) producer thread.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;let&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; cached_head&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; unsafe&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; *&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language z-self&quot;&gt;self&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;cached_head&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;get&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;() };&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The matching write is just as plain - a dereference and a store, no fences, no &lt;code&gt;compare_exchange&lt;&#x2F;code&gt;, no &lt;code&gt;fetch_*&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;&#x2F;&#x2F; SAFETY: same single-writer guarantee - only the producer writes here.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;unsafe&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; *&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language z-self&quot;&gt;self&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;cached_head&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;get&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;()&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt; head; }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;Why this is worth the &lt;code&gt;unsafe&lt;&#x2F;code&gt;:&lt;&#x2F;strong&gt; the compiler can now treat &lt;code&gt;cached_head&lt;&#x2F;code&gt; like an ordinary variable - keep it in a register across function calls, hoist it out of the reservation loop, fold it into the surrounding capacity arithmetic. An atomic would have barred every one of those.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;the-discipline-enforce-single-writer-outside-the-type&quot;&gt;The discipline: enforce single-writer outside the type&lt;&#x2F;h3&gt;
&lt;p&gt;The soundness of all this rests on one claim - &lt;em&gt;exactly one thread ever writes this field&lt;&#x2F;em&gt; - and that claim cannot be enforced by the &lt;code&gt;UnsafeCell&lt;&#x2F;code&gt; itself. It has to be guaranteed by the surrounding API. The cleanest way is to make the single-writer property &lt;em&gt;structural&lt;&#x2F;em&gt; rather than a rule humans must remember:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Give out a non-&lt;code&gt;Clone&lt;&#x2F;code&gt; producer handle. If the &lt;code&gt;Producer&lt;&#x2F;code&gt; type doesn&#x27;t implement &lt;code&gt;Clone&lt;&#x2F;code&gt;, it can&#x27;t be duplicated and handed to a second thread, so only one thread can ever hold the right to call &lt;code&gt;reserve()&lt;&#x2F;code&gt; - and therefore only one thread can ever write &lt;code&gt;cached_head&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;The type system now carries the invariant. There&#x27;s no comment to forget and no runtime check to pay for; constructing a second writer simply doesn&#x27;t compile.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;you-must-write-unsafe-impl-sync-by-hand-and-justify-it&quot;&gt;You must write &lt;code&gt;unsafe impl Sync&lt;&#x2F;code&gt; by hand - and justify it&lt;&#x2F;h3&gt;
&lt;p&gt;A type containing &lt;code&gt;UnsafeCell&amp;lt;T&amp;gt;&lt;&#x2F;code&gt; is deliberately not &lt;code&gt;Sync&lt;&#x2F;code&gt;. The standard library makes &lt;code&gt;UnsafeCell&lt;&#x2F;code&gt; opt out of &lt;code&gt;Sync&lt;&#x2F;code&gt; because unsynchronized interior mutability is the entire reason &lt;code&gt;UnsafeCell&lt;&#x2F;code&gt; exists. But sharing the ring across threads is the whole point of the structure, so you have to supply the impl yourself - and the value of doing it by hand is that it forces you to write down the proof:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;&#x2F;&#x2F; SAFETY:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;&#x2F;&#x2F; - `tail` and `cached_head` are written only by the producer thread.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;&#x2F;&#x2F; - `head` and `cached_tail` are written only by the consumer thread.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;&#x2F;&#x2F; - Cross-thread visibility of buffer slots is established only via the&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;&#x2F;&#x2F;   Release store on `tail` (producer) and the Acquire load on `tail`&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;&#x2F;&#x2F;   (consumer); the symmetric protocol applies to `head`.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;&#x2F;&#x2F; - Buffer-slot ownership is transferred by the [head, tail) range&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;&#x2F;&#x2F;   invariant: outside that range, slots are exclusively the producer&amp;#39;s&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;&#x2F;&#x2F;   to write; inside it, exclusively the consumer&amp;#39;s to read.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;&#x2F;&#x2F; - The backing storage is valid for cross-thread access and deallocation&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;&#x2F;&#x2F;   under this producer&#x2F;consumer protocol.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;&#x2F;&#x2F; - `T` may move across threads only when `T: Send`.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;unsafe impl&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;T&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Send&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; A&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; BufferAllocator&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; +&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Sync&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Sync&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; for&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Ring&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;T&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; A&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;gt; {}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;unsafe impl&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;T&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Send&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; A&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; BufferAllocator&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; +&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Send&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Send&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; for&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Ring&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;T&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; A&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;gt; {}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;A few things about this block are important for reasoning:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;The &lt;code&gt;T: Send&lt;&#x2F;code&gt; bound is mandatory.&lt;&#x2F;strong&gt; If &lt;code&gt;T&lt;&#x2F;code&gt; can&#x27;t cross threads, neither can a ring of &lt;code&gt;T&lt;&#x2F;code&gt;. Dropping the bound would let you transport a &lt;code&gt;!Send&lt;&#x2F;code&gt; type across a thread boundary inside the ring.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Forgetting the impls is a compile error.&lt;&#x2F;strong&gt; Forgetting the justification is a correctness bomb that compiles fine. Treat the &lt;code&gt;SAFETY:&lt;&#x2F;code&gt; comment as part of the code, not commentary on it.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Push proof obligations down to where they&#x27;re owned.&lt;&#x2F;strong&gt; The clause about backing-storage validity really belongs to the allocator&#x27;s contract, not to the ring. Stating it as an allocator-trait guarantee lets this impl rely on it instead of re-deriving allocator semantics inline.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;And the general discipline that ties Parts 1 and 2 together: &lt;strong&gt;every &lt;code&gt;unsafe&lt;&#x2F;code&gt; block gets a &lt;code&gt;&#x2F;&#x2F; SAFETY:&lt;&#x2F;code&gt; comment naming the specific invariant it relies on.&lt;&#x2F;strong&gt; Better still, give those invariants stable names (e.g. an &lt;code&gt;INV-…&lt;&#x2F;code&gt; identifier in a spec) and cite the name in the comment, so the chain from &quot;this code is sound&quot; to &quot;because this property holds&quot; is auditable end to end rather than folklore.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;where-single-writer-state-shows-up-beyond-ring-buffers&quot;&gt;Where single-writer state shows up beyond ring buffers&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;per-thread accumulators in scatter-gather pipelines (write locally, reduce once);&lt;&#x2F;li&gt;
&lt;li&gt;per-thread caches of read-mostly global state, refreshed only on notification;&lt;&#x2F;li&gt;
&lt;li&gt;exclusive slot access between a &lt;code&gt;reserve()&lt;&#x2F;code&gt; and its matching &lt;code&gt;commit()&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;the-two-ideas-are-one-idea&quot;&gt;The two ideas are one idea&lt;&#x2F;h2&gt;
&lt;p&gt;Minimum-sufficient ordering and single-writer state are the same principle applied at two levels. Ordering asks: &lt;em&gt;given that I need an atomic, what&#x27;s the cheapest correct contract with the hardware?&lt;&#x2F;em&gt; Single-writer asks the question one level up: &lt;em&gt;do I need the atomic at all?&lt;&#x2F;em&gt; In both cases the move is to pay for exactly the synchronization the algorithm requires and not one fence more. Loosely this can correspond to the more general principle of using the least powerful abstraction when it comes to software engineering.&lt;&#x2F;p&gt;
&lt;p&gt;There&#x27;s a third level to this same principle: sometimes you don&#x27;t need the cross-core &lt;em&gt;read&lt;&#x2F;em&gt; either, because a private, conservative snapshot of the other core&#x27;s cursor is good enough on the fast path. That snapshot is exactly the single-writer &lt;code&gt;UnsafeCell&lt;&#x2F;code&gt; cache introduced here. In Part 3 we will turn it into a throughput win.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Cache-Conscious Data Layout in Rust: Field Zoning, False Sharing, and the 128-Byte Rule</title>
          <pubDate>Mon, 29 Jun 2026 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://debasishg.github.io/blog/part1-cache-conscious-data-layout-in-rust/</link>
          <guid>https://debasishg.github.io/blog/part1-cache-conscious-data-layout-in-rust/</guid>
          <description xml:base="https://debasishg.github.io/blog/part1-cache-conscious-data-layout-in-rust/">&lt;h1 id=&quot;cache-conscious-data-layout-field-zoning-false-sharing-and-the-128-byte-rule&quot;&gt;Cache-Conscious Data Layout: Field Zoning, False Sharing, and the 128-Byte Rule&lt;&#x2F;h1&gt;
&lt;p&gt;Part 1 of &lt;strong&gt;Low-Level Systems Design in Rust&lt;&#x2F;strong&gt; - a series on writing high-throughput, low-latency systems code, using a single-producer &#x2F; single-consumer (SPSC) ring buffer as the running example.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;debasishg.github.io&#x2F;blog&#x2F;part0-architectural-decomposition-remove-contention-by-design&#x2F;&quot;&gt;Part 0 - &lt;em&gt;Architectural Decomposition&lt;&#x2F;em&gt;&lt;&#x2F;a&gt; made the highest-leverage decision (remove contention structurally, so every writer gets its own ring) and holds the full &lt;strong&gt;series index&lt;&#x2F;strong&gt; and guiding principles.&lt;&#x2F;p&gt;
&lt;p&gt;This post starts the micro-level work: given one such SPSC ring, how should it be laid out in memory? The patterns aren&#x27;t specific to ring buffers - they apply to any structure more than one core touches.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;the-thing-nobody-tells-you-about-multi-threaded-structs&quot;&gt;The thing nobody tells you about multi-threaded structs&lt;&#x2F;h2&gt;
&lt;p&gt;When you write a single-threaded data structure, the only layout question that matters is &quot;does it fit in cache.&quot; When you write a &lt;em&gt;multi-threaded&lt;&#x2F;em&gt; one, a second, relevant and a bit subtle question appears:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;For each field, &lt;strong&gt;which core touches it, and how often?&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Getting this wrong will make you pay in a way that no profiler flags as a single hot line. Two cores writing to &lt;em&gt;different&lt;&#x2F;em&gt; fields that happen to share a 64-byte cache line will quietly serialize each other through the hardware coherence protocol - a pathology called &lt;strong&gt;false sharing&lt;&#x2F;strong&gt;. The code looks lock-free. The benchmark says otherwise.&lt;&#x2F;p&gt;
&lt;p&gt;This post is about designing the layout deliberately. It comes in two parts:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Field zoning&lt;&#x2F;strong&gt; - grouping fields by &lt;code&gt;(write owner, frequency)&lt;&#x2F;code&gt; so that one core&#x27;s hot writes don&#x27;t evict another core&#x27;s hot working set.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Alignment and padding&lt;&#x2F;strong&gt; - the mechanics that make zoning real: why &lt;code&gt;#[repr(C)]&lt;&#x2F;code&gt; is load-bearing, why the magic number is often 128 and not 64, and why &lt;em&gt;adding&lt;&#x2F;em&gt; prefetch hints can make things worse.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;The running example is a single-producer &#x2F; single-consumer (SPSC) ring buffer.  One core (the producer) appends, another core (the consumer) drains. They coordinate through two monotonic cursors - &lt;code&gt;tail&lt;&#x2F;code&gt; (where the producer writes next) and &lt;code&gt;head&lt;&#x2F;code&gt; (where the consumer reads next).&lt;&#x2F;p&gt;
&lt;p&gt;Though the following discussion and the design principles are general enough, you can refer to this &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;debasishg&#x2F;ringmpsc-rs&#x2F;tree&#x2F;main&#x2F;crates&#x2F;ringmpsc&quot;&gt;ring buffer&lt;&#x2F;a&gt; implementation as a reference point that honors these guiding principles.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;part-1-field-zoning-design-based-on-who-touches-what&quot;&gt;Part 1 - Field zoning: design based on &quot;who touches what&quot;&lt;&#x2F;h2&gt;
&lt;p&gt;The cache line - commonly 64 bytes on x86-64 and many AArch64 cores - is the unit of currency for inter-core communication. Coherence traffic is accounted &lt;em&gt;per line&lt;&#x2F;em&gt;, not per byte or per field. So the first design move for any shared structure is to sort its fields into zones by access pattern:&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Zone&lt;&#x2F;th&gt;&lt;th&gt;Fields&lt;&#x2F;th&gt;&lt;th&gt;Write owner&lt;&#x2F;th&gt;&lt;th&gt;Cross-core access&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Producer-hot&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;tail&lt;&#x2F;code&gt;, &lt;code&gt;cached_head&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;producer&lt;&#x2F;td&gt;&lt;td&gt;consumer samples &lt;code&gt;tail&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Consumer-hot&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;head&lt;&#x2F;code&gt;, &lt;code&gt;cached_tail&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;consumer&lt;&#x2F;td&gt;&lt;td&gt;producer samples &lt;code&gt;head&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Cold&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;closed&lt;&#x2F;code&gt;, &lt;code&gt;config&lt;&#x2F;code&gt;, &lt;code&gt;metrics&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;lifecycle&#x2F;observability&lt;&#x2F;td&gt;&lt;td&gt;rare, not hot-path polling&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;&quot;Producer-hot&quot; does &lt;strong&gt;not&lt;&#x2F;strong&gt; mean &quot;producer-only.&quot; The producer owns writes to &lt;code&gt;tail&lt;&#x2F;code&gt;, but the consumer still reads &lt;code&gt;tail&lt;&#x2F;code&gt; when its cached view runs dry. The important distinction is &lt;strong&gt;write ownership&lt;&#x2F;strong&gt;: the producer is the core that keeps making the line exclusive, so the fields near that write must be chosen deliberately.&lt;&#x2F;p&gt;
&lt;p&gt;First, the vocabulary, since the rest of the post leans on it. The &lt;strong&gt;hot path&lt;&#x2F;strong&gt; is the code that runs on (almost) every operation - here, the send and receive loops that each core executes millions of times a second. &lt;strong&gt;Hot fields&lt;&#x2F;strong&gt; are the ones those loops touch, like &lt;code&gt;tail&lt;&#x2F;code&gt;, &lt;code&gt;head&lt;&#x2F;code&gt;, and the two cursor caches. The &lt;strong&gt;cold path&lt;&#x2F;strong&gt;, by contrast, is everything that runs rarely - construction, shutdown, an occasional metrics read - and &lt;strong&gt;cold fields&lt;&#x2F;strong&gt; are the ones only the cold path touches, like &lt;code&gt;config&lt;&#x2F;code&gt; and &lt;code&gt;metrics&lt;&#x2F;code&gt;. &quot;Hot&quot; and &quot;cold&quot; are about &lt;em&gt;frequency of access&lt;&#x2F;em&gt;, and they&#x27;re what the zones in the table above are sorted by.&lt;&#x2F;p&gt;
&lt;p&gt;The rule is then simple to state: &lt;strong&gt;place each zone on its own set of cache lines.&lt;&#x2F;strong&gt; A hot field written by one core must not share a line with hot fields another core reads or writes frequently - a producer store to &lt;code&gt;tail&lt;&#x2F;code&gt; should not invalidate the line holding the consumer&#x27;s &lt;code&gt;head&lt;&#x2F;code&gt; or &lt;code&gt;cached_tail&lt;&#x2F;code&gt;, and vice versa. Cold fields, since nobody contends over them on the hot path, can stay packed together at the back.&lt;&#x2F;p&gt;
&lt;p&gt;Here is a ring buffer that encodes those zones directly in its declaration. The type parameter &lt;code&gt;A&lt;&#x2F;code&gt; is just a pluggable allocator for the backing buffer, ignore it if you like - what matters is the &lt;em&gt;order and grouping&lt;&#x2F;em&gt; of the fields:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;#[&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-attribute z-rust&quot;&gt;repr&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;C&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;pub&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt; struct&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Ring&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;T&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; A&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; BufferAllocator&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; HeapAllocator&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;    &#x2F;&#x2F; PRODUCER HOT &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    tail&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;        CacheAligned&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;AtomicU64&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    cached_head&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; CacheAligned&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;UnsafeCell&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;u64&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;gt;&amp;gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;    &#x2F;&#x2F; CONSUMER HOT&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    head&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;        CacheAligned&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;AtomicU64&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    cached_tail&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; CacheAligned&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;UnsafeCell&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;u64&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;gt;&amp;gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;    &#x2F;&#x2F; COLD&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    closed&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;      AtomicBool&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    metrics&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;     Metrics&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    config&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;      Config&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    buffer&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;      UnsafeCell&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;A&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;Buffer&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;T&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;gt;&amp;gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;A few things are worth reading carefully here, because each one is a deliberate choice rather than an accident of style.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;The two &lt;code&gt;cached_*&lt;&#x2F;code&gt; fields live in the hot zones, not the cold one.&lt;&#x2F;strong&gt; A producer that wants to know &quot;is there space?&quot; must compare &lt;code&gt;tail&lt;&#x2F;code&gt; against &lt;code&gt;head&lt;&#x2F;code&gt;. But &lt;code&gt;head&lt;&#x2F;code&gt; is written by the &lt;em&gt;other&lt;&#x2F;em&gt; core, so reading it directly is a potential cross-core coherence miss - tens to hundreds of cycles. Instead, the producer keeps &lt;code&gt;cached_head&lt;&#x2F;code&gt;: a single-writer, producer-owned snapshot of where the consumer was &lt;em&gt;last time we looked&lt;&#x2F;em&gt;. Because a stale view of &lt;code&gt;head&lt;&#x2F;code&gt; can only ever &lt;em&gt;under-report&lt;&#x2F;em&gt; available space (never over-report it), the cache is always safe to trust on the fast path, and the expensive &lt;code&gt;Acquire&lt;&#x2F;code&gt; read of the real &lt;code&gt;head&lt;&#x2F;code&gt; fires only when the cache says we&#x27;re out of room. That snapshot is touched on &lt;em&gt;every&lt;&#x2F;em&gt; send, so it belongs in the producer-hot zone - and it is deliberately a plain &lt;code&gt;UnsafeCell&amp;lt;u64&amp;gt;&lt;&#x2F;code&gt;, not an atomic, precisely because only one core ever writes it. The consumer has the mirror image: &lt;code&gt;cached_tail&lt;&#x2F;code&gt; lets it avoid reading the producer-owned &lt;code&gt;tail&lt;&#x2F;code&gt; until the local snapshot is exhausted.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Cold fields share lines on purpose.&lt;&#x2F;strong&gt; &lt;code&gt;closed&lt;&#x2F;code&gt;, &lt;code&gt;metrics&lt;&#x2F;code&gt;, and &lt;code&gt;config&lt;&#x2F;code&gt; are packed together with no padding between them. That&#x27;s not laziness - it&#x27;s the point. The goal of zoning is not to align everything; it&#x27;s to align &lt;em&gt;only the things that ping between cores&lt;&#x2F;em&gt;. Padding a cold field just wastes a cache line you could have spent keeping hot data resident. If a lifecycle flag is polled on every send or receive, it is no longer cold; promote it into its own hot or lifecycle zone instead of hiding it behind the word &quot;shutdown.&quot;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;why-repr-c-is-doing-real-work&quot;&gt;Why &lt;code&gt;#[repr(C)]&lt;&#x2F;code&gt; is doing real work&lt;&#x2F;h3&gt;
&lt;p&gt;Notice the &lt;code&gt;#[repr(C)]&lt;&#x2F;code&gt; on the struct. It is not decorative, and removing it would silently undermine the entire layout strategy.&lt;&#x2F;p&gt;
&lt;p&gt;By default, Rust uses &lt;code&gt;repr(Rust)&lt;&#x2F;code&gt;, and the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;reference&#x2F;type-layout.html#the-rust-representation&quot;&gt;Rust Reference is explicit&lt;&#x2F;a&gt; that &lt;code&gt;repr(Rust)&lt;&#x2F;code&gt; guarantees only field &lt;em&gt;alignment&lt;&#x2F;em&gt;, that fields don&#x27;t overlap, and that the aggregate is sufficiently aligned. It explicitly does &lt;strong&gt;not&lt;&#x2F;strong&gt; guarantee that fields are laid out in declaration order - the compiler is free to reorder them (typically to minimize padding). For a normal struct that&#x27;s a feature. For a struct whose correctness-of-performance depends on &lt;code&gt;tail&lt;&#x2F;code&gt; and &lt;code&gt;head&lt;&#x2F;code&gt; sitting in &lt;em&gt;different&lt;&#x2F;em&gt; zones, a reorder could collapse your carefully separated zones back onto shared lines.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;#[repr(C)]&lt;&#x2F;code&gt; pins fields in declaration order, so the zone layout you wrote is the zone layout you get.&lt;&#x2F;p&gt;
&lt;p&gt;One subtlety that&#x27;s easy to miss: &lt;strong&gt;&lt;code&gt;repr(C)&lt;&#x2F;code&gt; on the outer struct does not recursively freeze the layout of nested &lt;code&gt;repr(Rust)&lt;&#x2F;code&gt; fields.&lt;&#x2F;strong&gt; It fixes the order of &lt;code&gt;Ring&lt;&#x2F;code&gt;&#x27;s own fields, but the internal ordering of, say, &lt;code&gt;Metrics&lt;&#x2F;code&gt; or &lt;code&gt;Config&lt;&#x2F;code&gt; is still &lt;code&gt;repr(Rust)&lt;&#x2F;code&gt; unless those types carry their own &lt;code&gt;repr(C)&lt;&#x2F;code&gt;. If a nested struct has its &lt;em&gt;own&lt;&#x2F;em&gt; hot&#x2F;cold split that matters, give it &lt;code&gt;repr(C)&lt;&#x2F;code&gt; too.  Here it doesn&#x27;t matter, because &lt;code&gt;Metrics&lt;&#x2F;code&gt; and &lt;code&gt;Config&lt;&#x2F;code&gt; are entirely cold.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;part-2-alignment-and-false-sharing-making-zoning-real&quot;&gt;Part 2 - Alignment and false sharing: making zoning real&lt;&#x2F;h2&gt;
&lt;p&gt;Zoning is the &lt;em&gt;intent&lt;&#x2F;em&gt;. Alignment is the &lt;em&gt;mechanism&lt;&#x2F;em&gt;. Declaring that &lt;code&gt;tail&lt;&#x2F;code&gt; and &lt;code&gt;head&lt;&#x2F;code&gt; belong to different zones accomplishes nothing unless the bytes actually land on different cache lines. That&#x27;s the job of &lt;code&gt;CacheAligned&amp;lt;T&amp;gt;&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;the-one-line-helper-that-pays-for-itself&quot;&gt;The one-line helper that pays for itself&lt;&#x2F;h3&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;&#x2F;&#x2F;&#x2F; Aligns a value to a 128-byte boundary so that cross-core fields don&amp;#39;t&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;&#x2F;&#x2F;&#x2F; share a cache line. The 128 (vs. 64) is a target policy: it also leaves&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;&#x2F;&#x2F;&#x2F; room for adjacent-line&#x2F;spatial-prefetch effects on CPUs where those matter.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;#[&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-attribute z-rust&quot;&gt;repr&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;C&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-attribute z-rust&quot;&gt; align&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-attribute z-rust&quot;&gt;128&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;))]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;struct&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; CacheAligned&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;T&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    value&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; T&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;impl&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;T&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; CacheAligned&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;T&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage&quot;&gt;    const&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; fn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; new&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-variable&quot;&gt;(value&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; T&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language z-self&quot;&gt; Self&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-language z-self&quot;&gt;        Self&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-variable&quot;&gt; { value }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;impl&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;T&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-namespace&quot;&gt; std&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-namespace&quot;&gt;ops&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;Deref&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; for&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; CacheAligned&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;T&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;    type&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Target&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; T&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    fn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; deref&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language z-self&quot;&gt;self&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; -&amp;gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language z-self&quot;&gt;Self&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;Target&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;        &amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language z-self&quot;&gt;self&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;value&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;That&#x27;s the whole thing - a &lt;code&gt;#[repr(C, align(128))]&lt;&#x2F;code&gt; newtype with a &lt;code&gt;Deref&lt;&#x2F;code&gt; so it&#x27;s transparent at the call site (&lt;code&gt;self.tail.load(...)&lt;&#x2F;code&gt; just works). No library dependency required. Because &lt;code&gt;Ring&lt;&#x2F;code&gt; is &lt;code&gt;repr(C)&lt;&#x2F;code&gt;, the compiler lays fields out in declaration order and inserts whatever padding is needed to satisfy each field&#x27;s alignment. Because a Rust type&#x27;s size is a multiple of its alignment, each small &lt;code&gt;CacheAligned&amp;lt;T&amp;gt;&lt;&#x2F;code&gt; also consumes a full 128-byte slot. No two hot fields can share a line, and - critically - neither can they share a line with the &lt;em&gt;cold&lt;&#x2F;em&gt; fields that follow.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;why-128-bytes-when-the-cache-line-is-64&quot;&gt;Why 128 bytes, when the cache line is 64?&lt;&#x2F;h3&gt;
&lt;p&gt;This is the part that trips people up. If the cache line is 64 bytes, why pad to 128?&lt;&#x2F;p&gt;
&lt;p&gt;Because strict line-level separation can still be too optimistic. Some CPUs have adjacent-line or spatial prefetch behavior: when you touch one 64-byte line, the core may speculatively pull in its neighbor. If producer-hot data sits on the line immediately adjacent to consumer-hot data, the prefetcher can drag the two into each other&#x27;s caches even though, strictly speaking, they never shared a line. The result is &lt;strong&gt;prefetcher-induced false sharing&lt;&#x2F;strong&gt;: the lines are technically separate, the contention is real, and line-granularity tools may not point directly at it. You see it as throughput that&#x27;s lower than the algorithm predicts.&lt;&#x2F;p&gt;
&lt;p&gt;For small cursor fields, aligning each hot field to 128 bytes makes it start on an even 64-byte-line boundary, so an adjacent-line prefetch usually pulls in padding rather than another core&#x27;s hot data.&lt;&#x2F;p&gt;
&lt;p&gt;Treat the exact width as a &lt;strong&gt;target-specific policy, not a universal constant&lt;&#x2F;strong&gt;.
Crossbeam&#x27;s current &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.rs&#x2F;crossbeam-utils&#x2F;latest&#x2F;crossbeam_utils&#x2F;struct.CachePadded.html&quot;&gt;&lt;code&gt;CachePadded&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; policy is:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;128 bytes&lt;&#x2F;strong&gt; on x86-64, AArch64, and powerpc64,&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;256 bytes&lt;&#x2F;strong&gt; on s390x,&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;32 bytes&lt;&#x2F;strong&gt; on arm, mips, mips64, sparc, and hexagon,&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;16 bytes&lt;&#x2F;strong&gt; on m68k,&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;64 bytes&lt;&#x2F;strong&gt; on everything else.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Crossbeam also documents that these are reasonable guesses, not a guarantee that they match the physical cache line of the machine you&#x27;re running on. A local &lt;code&gt;CacheAligned&amp;lt;T&amp;gt;&lt;&#x2F;code&gt; with a fixed &lt;code&gt;align(128)&lt;&#x2F;code&gt; is appropriate when you specifically want to &lt;em&gt;pin&lt;&#x2F;em&gt; the policy (e.g. you&#x27;ve benchmarked your target and want it frozen).  Either way: &lt;strong&gt;verify with benchmarks&lt;&#x2F;strong&gt;, don&#x27;t trust any single number - including 128.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;putting-it-together-the-layout-the-cpu-actually-sees&quot;&gt;Putting it together: the layout the CPU actually sees&lt;&#x2F;h3&gt;
&lt;p&gt;With the zones and the alignment combined, construction is unremarkable - the layout work has already been done by the types:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;Ring&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    tail&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;        CacheAligned&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;new&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-namespace&quot;&gt;AtomicU64&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;new&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;0&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    cached_head&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; CacheAligned&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;new&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-namespace&quot;&gt;UnsafeCell&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;new&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;0&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    head&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;        CacheAligned&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;new&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-namespace&quot;&gt;AtomicU64&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;new&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;0&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    cached_tail&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; CacheAligned&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;new&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-namespace&quot;&gt;UnsafeCell&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;new&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;0&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    closed&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;      AtomicBool&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;new&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-language&quot;&gt;false&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;    &#x2F;&#x2F; metrics, config, buffer ...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The producer core writes the &lt;code&gt;tail&lt;&#x2F;code&gt; line and uses the &lt;code&gt;cached_head&lt;&#x2F;code&gt; line. The consumer core writes the &lt;code&gt;head&lt;&#x2F;code&gt; line and uses the &lt;code&gt;cached_tail&lt;&#x2F;code&gt; line. The published cursors still create the unavoidable cross-core communication - the consumer must sometimes observe &lt;code&gt;tail&lt;&#x2F;code&gt;, and the producer must sometimes observe &lt;code&gt;head&lt;&#x2F;code&gt; - but those invalidations no longer drag unrelated hot fields with them.  That is the payoff, and it costs roughly one 128-byte slot per hot field: a good trade for a structure hammered by two cores, and one you&#x27;d never make for a cold field.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;two-counterintuitive-corollaries&quot;&gt;Two counterintuitive corollaries&lt;&#x2F;h2&gt;
&lt;p&gt;Once you&#x27;re thinking at the level of cache lines and prefetchers, two pieces of &quot;obvious&quot; performance folklore turn out to be wrong.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;don-t-sprinkle-in-software-prefetch-hints&quot;&gt;Don&#x27;t sprinkle in software prefetch hints&lt;&#x2F;h3&gt;
&lt;p&gt;A common instinct, having learned that the prefetcher exists, is to &quot;help&quot; it: drop &lt;code&gt;_mm_prefetch&lt;&#x2F;code&gt; (or Rust&#x27;s &lt;code&gt;core::intrinsics::prefetch_*&lt;&#x2F;code&gt;) into the hot loop to pull the next slots in early. On a ring buffer - i.e. &lt;strong&gt;stride-1, linear access&lt;&#x2F;strong&gt; - this is almost always a &lt;em&gt;loss&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Modern L1&#x2F;L2 hardware prefetchers detect linear strides within the first 2–3 accesses and stream cache lines in ahead of the CPU automatically. A manual software prefetch doesn&#x27;t add to that; it &lt;em&gt;competes&lt;&#x2F;em&gt; with it - consuming instruction issue slots and memory bandwidth that the demand fetches actually need, while the hardware was already going to bring those lines in. It&#x27;s a mistake I&#x27;ve watched ship more than once: a hot loop seeded with manual prefetch hints, A&#x2F;B benchmarked, and found to &lt;em&gt;hurt&lt;&#x2F;em&gt; throughput - the hints come back out.&lt;&#x2F;p&gt;
&lt;p&gt;The honest rule: software prefetches earn their keep only for &lt;strong&gt;non-linear&lt;&#x2F;strong&gt; access patterns - graph walks, hash-table probe sequences, pointer chasing - where the hardware prefetcher &lt;em&gt;can&#x27;t&lt;&#x2F;em&gt; predict the next address. And even then, only add them if a profile-guided experiment proves they help on your workload. For anything stride-1, get out of the prefetcher&#x27;s way.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;pick-a-capacity-that-fits-a-cache-level-deliberately&quot;&gt;Pick a capacity that fits a cache level - deliberately&lt;&#x2F;h3&gt;
&lt;p&gt;The ring&#x27;s &lt;em&gt;buffer&lt;&#x2F;em&gt; has its own cache story, separate from the cursors. Capacity doesn&#x27;t guarantee residency in a particular cache level, but it does set the working-set budget you are asking the hierarchy to carry. Choose it consciously rather than typing a round number and letting the hardware discover the trade:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Sized to stay near L1.&lt;&#x2F;strong&gt; For 8-byte elements, 4,096 slots is 32 KiB. That can be a reasonable starting point for a &lt;em&gt;cache-resident&lt;&#x2F;em&gt; ring on cores with a 32 KiB-or-larger L1D, but it leaves no room for other hot data on a 32 KiB L1.  L1D sizes vary, and fitting capacity-wise does &lt;strong&gt;not&lt;&#x2F;strong&gt; guarantee zero L2&#x2F;L3 misses - conflict misses, prefetch behavior, coherence traffic, and other hot data on the same core all still matter. Confirm with hardware counters, not arithmetic.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Sized to absorb bursts.&lt;&#x2F;strong&gt; Sizing at 256K slots (2 MiB for 8-byte elements) deliberately stops pretending the buffer is L1-resident and accepts lower-cache or LLC latency in exchange for soaking up much larger bursts without backpressure.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Both are legitimate. What isn&#x27;t legitimate is letting &quot;a nice round number like one million&quot; sneak into your config and &lt;em&gt;accidentally&lt;&#x2F;em&gt; deciding the working set your hot path has to carry.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;where-this-generalizes&quot;&gt;Where this generalizes&lt;&#x2F;h2&gt;
&lt;p&gt;None of this is specific to ring buffers. The &quot;zone by &lt;code&gt;(write owner, frequency)&lt;&#x2F;code&gt;, then pad the cross-core fields&quot; pattern shows up wherever a structure is shared across cores:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;the &lt;code&gt;tail&lt;&#x2F;code&gt;&#x2F;&lt;code&gt;head&lt;&#x2F;code&gt; cursors of &lt;strong&gt;SPSC&lt;&#x2F;strong&gt; queues, and the contended cursors of &lt;strong&gt;MPSC&lt;&#x2F;strong&gt; queues (padding prevents false sharing there; it does not remove the shared-tail contention),&lt;&#x2F;li&gt;
&lt;li&gt;per-thread counters in &lt;strong&gt;sharded metrics&lt;&#x2F;strong&gt; (an &lt;code&gt;AtomicU64&lt;&#x2F;code&gt; per core, summed on read),&lt;&#x2F;li&gt;
&lt;li&gt;per-core &lt;strong&gt;epoch counters&lt;&#x2F;strong&gt; in EBR &#x2F; hazard-pointer memory reclamation,&lt;&#x2F;li&gt;
&lt;li&gt;per-CPU &lt;strong&gt;scheduler run queues&lt;&#x2F;strong&gt;,&lt;&#x2F;li&gt;
&lt;li&gt;resize-epoch counters in &lt;strong&gt;lock-free hash tables&lt;&#x2F;strong&gt;.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;In every case the recipe is the same:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;For each field, name the write owner, the readers, and the frequency.&lt;&#x2F;li&gt;
&lt;li&gt;Group fields into hot zones (one per write owner, plus isolated zones for any unavoidable multi-writer atomics) and a cold zone.&lt;&#x2F;li&gt;
&lt;li&gt;Lock the order with &lt;code&gt;#[repr(C)]&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;Pad the hot, cross-core fields to a target-appropriate cache boundary - 128 bytes on x86-64&#x2F;AArch64&#x2F;powerpc64 in Crossbeam&#x27;s policy, or let &lt;code&gt;CachePadded&lt;&#x2F;code&gt; pick.&lt;&#x2F;li&gt;
&lt;li&gt;Leave the cold fields packed, and &lt;em&gt;don&#x27;t&lt;&#x2F;em&gt; add prefetch hints to linear loops.&lt;&#x2F;li&gt;
&lt;li&gt;Verify the win with hardware counters such as &lt;code&gt;perf c2c&lt;&#x2F;code&gt; where available, and with a throughput benchmark - because the bugs this prevents are exactly the ones a profiler won&#x27;t point at for you.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;The structure looks lock-free either way. The difference between the two versions is whether the hardware agrees.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Architectural Decomposition: Remove Contention by Design</title>
          <pubDate>Mon, 22 Jun 2026 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://debasishg.github.io/blog/part0-architectural-decomposition-remove-contention-by-design/</link>
          <guid>https://debasishg.github.io/blog/part0-architectural-decomposition-remove-contention-by-design/</guid>
          <description xml:base="https://debasishg.github.io/blog/part0-architectural-decomposition-remove-contention-by-design/">&lt;h1 id=&quot;architectural-decomposition-remove-contention-by-design&quot;&gt;Architectural Decomposition: Remove Contention by Design&lt;&#x2F;h1&gt;
&lt;p&gt;Across the Rust projects I&#x27;ve worked on, the ones that demanded high throughput and low latency were almost always built around a few architectural principles that remained invariant across all of them. Many of these principles are not limited to Rust, but apply equally, with implementation-specific variations, to lower-level languages like C++ or Zig.&lt;&#x2F;p&gt;
&lt;p&gt;In this series of blog posts I plan to mine those design principles and prepare a collection of patterns that can be applied in similar contexts. I call the series &lt;strong&gt;Low-Level Systems Design in Rust&lt;&#x2F;strong&gt; - this is the opening installment of it. Most of the posts work at the micro level - cache lines, memory ordering, allocation - using a single-producer &#x2F; single-consumer (SPSC) ring buffer as the running example.&lt;&#x2F;p&gt;
&lt;p&gt;But before any of that pays off, there&#x27;s one decision that dwarfs all the others, and it&#x27;s structural. This post is about that decision. The patterns aren&#x27;t specific to ring buffers: they apply to any system where many writers feed a shared reader.&lt;&#x2F;p&gt;
&lt;p&gt;Three principles run underneath everything that follows, and the first one &lt;em&gt;is&lt;&#x2F;em&gt; this post:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;The biggest wins are architectural, not microscopic.&lt;&#x2F;strong&gt; Eliminating a shared write cursor is worth orders of magnitude more than picking the &quot;right&quot; &lt;code&gt;Ordering&lt;&#x2F;code&gt;. Cache-line padding, atomic tuning, and inlining only matter &lt;em&gt;after&lt;&#x2F;em&gt; you&#x27;ve made the structural choice that removes contention in the first place.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Optimize what the hardware actually does, not what the source looks like.&lt;&#x2F;strong&gt; Modern CPUs speculate past branches, reorder loads, and bounce cache lines between cores under contention. Many also have adjacent-line or spatial prefetch behavior that can make a nominally &quot;separate&quot; cache line relevant to performance. Source-level cleverness that ignores those realities buys nothing.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Measure, don&#x27;t guess.&lt;&#x2F;strong&gt; Every technique in this series earns its place against a benchmark - and benchmark the code you &lt;em&gt;think&lt;&#x2F;em&gt; you&#x27;re benchmarking: Rust&#x27;s optimizer is happy to delete work whose result is unused, so reach for &lt;code&gt;std::hint::black_box&lt;&#x2F;code&gt; and read the generated assembly before trusting any number.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Here&#x27;s a rough idea of what I plan to cover as part of this series:&lt;&#x2F;p&gt;
&lt;ol start=&quot;0&quot;&gt;
&lt;li&gt;&lt;strong&gt;Architectural Decomposition&lt;&#x2F;strong&gt; &lt;em&gt;(this post)&lt;&#x2F;em&gt; - remove contention by design.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Cache-Conscious Data Layout&lt;&#x2F;strong&gt; - field zoning, false sharing, and cache-line and adjacent-line placement heuristics.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;The Cross-Core Contract&lt;&#x2F;strong&gt; - memory ordering and single-writer state.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Amortizing Cross-Core Coordination&lt;&#x2F;strong&gt; - cursor caching and batch processing.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Zero-Copy on the Hot Path&lt;&#x2F;strong&gt; - reserve&#x2F;commit and fast-path&#x2F;slow-path splitting.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Compile-Time Leverage&lt;&#x2F;strong&gt; - specialization, branch &amp;amp; arithmetic hygiene, and inlining.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Backoff &amp;amp; Memory Provisioning&lt;&#x2F;strong&gt; - adaptive spinning, custom allocators, NUMA &amp;amp; huge pages.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Safety as Performance&lt;&#x2F;strong&gt; - moves, the borrow checker, &lt;code&gt;debug_assert!&lt;&#x2F;code&gt;, and cold-path hygiene.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Trustworthy Performance&lt;&#x2F;strong&gt; - verification, idioms, anti-patterns, and knowing when to stop.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Start here; the rest builds on the foundation this post lays.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;the-most-expensive-instruction-is-the-one-two-cores-fight-over&quot;&gt;The most expensive instruction is the one two cores fight over&lt;&#x2F;h2&gt;
&lt;p&gt;Here&#x27;s the scenario that drives the whole design. You have &lt;em&gt;N&lt;&#x2F;em&gt; independent producers - threads, tasks, cores - that all need to hand work to a single consumer. Assume a bounded in-memory handoff path where per-producer FIFO is acceptable and the consumer can poll. The obvious implementation is one shared multi-producer queue: a single buffer with shared write-side state that every producer must update, often through an atomic read-modify-write such as a compare-and-swap or &lt;code&gt;fetch_add&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;That obvious implementation has a problem that gets &lt;em&gt;worse&lt;&#x2F;em&gt; as you add cores. Every producer is hammering the same &lt;code&gt;tail&lt;&#x2F;code&gt; cache line, or whatever cache line holds the shared write-side state. On a CAS or other read-modify-write, the line has to be in the &lt;strong&gt;exclusive&lt;&#x2F;strong&gt; coherence state on the writing core - which means other cached copies of that line must be &lt;strong&gt;invalidated&lt;&#x2F;strong&gt; before the write can complete. Two producers that want to append at the same instant don&#x27;t run in parallel; the hardware coherence protocol serializes them, ping-ponging ownership of that one line from core to core. Add a third producer and it&#x27;s worse. The shared cursor is a contention point, and the cost of touching it scales with the number of cores touching it.&lt;&#x2F;p&gt;
&lt;p&gt;This is the trap: the code &lt;em&gt;looks&lt;&#x2F;em&gt; lock-free - there&#x27;s no mutex anywhere - but the shared atomic cursor reintroduces serialization at the hardware level. You&#x27;ve moved the lock from the OS into the cache-coherence fabric, where it&#x27;s harder to see and you can&#x27;t even name it.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-decomposition-give-every-writer-its-own-lane&quot;&gt;The decomposition: give every writer its own lane&lt;&#x2F;h2&gt;
&lt;p&gt;The fix is to refuse the premise. Don&#x27;t make independent writers share a structure at all. Give &lt;strong&gt;each writer its own private single-writer structure&lt;&#x2F;strong&gt;, and let the consumer poll across them:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Producer 0 ──► [ SPSC Ring 0 ] ──┐&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Producer 1 ──► [ SPSC Ring 1 ] ──┼──► Consumer (sweeps the rings in turn)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Producer 2 ──► [ SPSC Ring 2 ] ──┘&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        … one dedicated ring per producer …&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now each producer writes only to &lt;em&gt;its own&lt;&#x2F;em&gt; ring. A producer&#x27;s append updates a cursor no other producer ever touches. In Rust that cursor is still an atomic value, because the consumer reads it from another thread, but the hot update is a simple atomic &lt;code&gt;store&lt;&#x2F;code&gt;, not an atomic read-modify-write. The result: &lt;strong&gt;no CAS, no spin loop, and crucially no producer-producer cache-line bouncing.&lt;&#x2F;strong&gt; Two producers appending at the same instant genuinely run in parallel, because they&#x27;re writing to different cache lines on different rings. The defining property:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;N&lt;&#x2F;em&gt; independent single-writer structures have &lt;strong&gt;zero&lt;&#x2F;strong&gt; coherence traffic on the hot cursor path &lt;em&gt;between the writers&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;The contention didn&#x27;t vanish into thin air - it was &lt;em&gt;converted&lt;&#x2F;em&gt;. Instead of &lt;em&gt;N&lt;&#x2F;em&gt; cores serializing on one hot cursor, you have one consumer paying an &lt;strong&gt;O(N) sweep&lt;&#x2F;strong&gt; to poll the rings in turn. That&#x27;s a fundamentally better trade when producer throughput dominates: a single thread doing a little more sequential work, in exchange for eliminating the quadratic-feeling coherence storm among the writers. Each individual ring is then a clean single-producer &#x2F; single-consumer problem - which is exactly the structure the rest of this series spends its time optimizing.&lt;&#x2F;p&gt;
&lt;p&gt;This decomposition also changes the semantics. Each ring preserves FIFO order for one producer, but the system no longer has a natural global enqueue order across all producers. If the consumer needs a total order, you need sequence numbers, timestamps, or a merge step - and that reconciliation has its own cost.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;be-honest-about-the-trade-offs&quot;&gt;Be honest about the trade-offs&lt;&#x2F;h2&gt;
&lt;p&gt;Decomposition isn&#x27;t free, and the costs are worth stating plainly:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Memory grows with N.&lt;&#x2F;strong&gt; One buffer per producer instead of one shared buffer.  For a few dozen producers this is negligible; for tens of thousands it&#x27;s a real budget line.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Single-message latency can rise.&lt;&#x2F;strong&gt; The consumer has to &lt;em&gt;poll&lt;&#x2F;em&gt; - a message sitting in ring 3 isn&#x27;t seen until the sweep reaches ring 3. For small, hot sets of rings that overhead is often tiny compared with the contention it eliminates, but the exact number is hardware- and workload-dependent. For very large N you may need a smarter consumer: a ready-set, per-core consumers, or an event mechanism, all of which I will return to in later posts.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Backpressure becomes per-lane.&lt;&#x2F;strong&gt; If one producer&#x27;s ring is full, that producer has to block, drop, or spill somewhere else, while other producers may continue normally. That isolation is often useful, but it is a policy decision, not a free consequence.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Fairness is now explicit.&lt;&#x2F;strong&gt; A consumer that drains one busy ring for too long can starve quieter rings. The sweep policy - round-robin, bounded drain, priority, batching - becomes part of the design.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Global ordering is no longer free.&lt;&#x2F;strong&gt; Per-producer FIFO is cheap. Cross-producer total order requires extra metadata and merge work.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;When to reach for it:&lt;&#x2F;strong&gt; any time the asymmetry between writers and readers is large - &lt;em&gt;many&lt;&#x2F;em&gt; writers, &lt;em&gt;one&lt;&#x2F;em&gt; (or few) readers - and the readers can tolerate an O(N) sweep. That&#x27;s precisely the shape where a shared cursor hurts most and a per-writer structure helps most.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;you-ve-seen-this-pattern-before&quot;&gt;You&#x27;ve seen this pattern before&lt;&#x2F;h2&gt;
&lt;p&gt;Architectural decomposition is one of the most widely reinvented ideas in systems software, because the writer&#x2F;reader asymmetry is everywhere:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Allocator sharding&lt;&#x2F;strong&gt; - jemalloc uses arenas and thread caches, while mimalloc shards free lists and separates thread-local from concurrent free paths, so allocation and free traffic doesn&#x27;t collapse onto one global structure.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Per-worker run queues&lt;&#x2F;strong&gt; - schedulers like Tokio and Go keep local queues around workers, with global queues and work-stealing as reconciliation mechanisms instead of putting every operation through one central queue.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Sharded counters&lt;&#x2F;strong&gt; - one &lt;code&gt;AtomicU64&lt;&#x2F;code&gt; per core, summed on read, so the hot increment path never contends. (The thread-local metrics aggregation in Part 8 is the same idea taken one step further.)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Per-writer log segments&lt;&#x2F;strong&gt; - write-ahead logs that give each writer its own segment and merge at commit time.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;In every case the move is identical: take a shared mutable thing that &lt;em&gt;N&lt;&#x2F;em&gt; agents fight over, and replace it with &lt;em&gt;N&lt;&#x2F;em&gt; private things plus a cheap reconciliation step on the read side.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;why-this-comes-first&quot;&gt;Why this comes first&lt;&#x2F;h2&gt;
&lt;p&gt;It&#x27;s tempting to open a performance series with the satisfying micro-techniques - cache-line padding, the &lt;code&gt;Relaxed&lt;&#x2F;code&gt; versus &lt;code&gt;Acquire&lt;&#x2F;code&gt; decision, the zero-copy reservation. But those are refinements &lt;em&gt;within&lt;&#x2F;em&gt; a structure, and refining a structure that&#x27;s architecturally contended is polishing a part that shouldn&#x27;t exist. The padding in Part 1 matters &lt;em&gt;because&lt;&#x2F;em&gt; there&#x27;s no longer a shared cursor competing for the same cache lines; the minimum-sufficient ordering in Part 2 is tractable &lt;em&gt;because&lt;&#x2F;em&gt; each ring has exactly one writer and one reader.&lt;&#x2F;p&gt;
&lt;p&gt;So this is the decision that makes the rest of the series worth doing. Get it right and every later technique compounds on a clean foundation. Get it wrong - keep the shared cursor - and no amount of cache-line tuning will save you from the coherence traffic you designed in.&lt;&#x2F;p&gt;
&lt;p&gt;From here on, the running example is a single one of those SPSC rings, examined up close. Part 1 starts where it should: how to lay that ring out in memory so two cores never fight over a cache line.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Scalar Replacement of Aggregates: How &quot;Copy to Locals&quot; Unlocks the Compiler</title>
          <pubDate>Mon, 15 Jun 2026 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://debasishg.github.io/blog/sroa/</link>
          <guid>https://debasishg.github.io/blog/sroa/</guid>
          <description xml:base="https://debasishg.github.io/blog/sroa/">&lt;h1 id=&quot;scalar-replacement-of-aggregates-how-copy-to-locals-unlocks-the-compiler&quot;&gt;Scalar Replacement of Aggregates: How &quot;Copy to Locals&quot; Unlocks the Compiler&lt;&#x2F;h1&gt;
&lt;p&gt;Some performance fixes look almost suspiciously small in the diff. You change a couple of lines, the code still says the same thing to a human reader, and a hot loop suddenly has far less memory traffic.&lt;&#x2F;p&gt;
&lt;p&gt;TigerBeetle&#x27;s &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tigerbeetle&#x2F;tigerbeetle&#x2F;pull&#x2F;3201&quot;&gt;PR #3201&lt;&#x2F;a&gt; is one of those fixes. It changed the AEGIS-128L state update in &lt;code&gt;src&#x2F;stdx&#x2F;aegis.zig&lt;&#x2F;code&gt; from &quot;operate through a pointer to &lt;code&gt;state.blocks&lt;&#x2F;code&gt;&quot; to &quot;copy the eight lanes into a local array, update the local array, then write it back at the boundary.&quot; The PR reported about a 2x improvement in the AEGIS microbenchmark and a 3-10% end-to-end throughput improvement in the measured TigerBeetle workloads.&lt;&#x2F;p&gt;
&lt;p&gt;The compiler idea behind this is &lt;strong&gt;SROA&lt;&#x2F;strong&gt;, or &lt;strong&gt;Scalar Replacement of Aggregates&lt;&#x2F;strong&gt;. More precisely, this is a manual SROA-friendly code shape: the source code removes an aliasing and visibility obstacle so LLVM can keep the hot state in registers instead of repeatedly loading and storing it.&lt;&#x2F;p&gt;
&lt;p&gt;This post explains what SROA is, why pointers and opaque memory effects get in the way, and how the &quot;copy to locals, work, write back&quot; shape helps. Then we will build small Zig and Rust reproductions and connect the result back to TigerBeetle&#x27;s AEGIS code.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;1-what-sroa-actually-means&quot;&gt;1. What SROA Actually Means&lt;&#x2F;h2&gt;
&lt;p&gt;An &lt;strong&gt;aggregate&lt;&#x2F;strong&gt; is a value made from smaller values: a struct, array, tuple, or record. &lt;strong&gt;Scalar replacement of aggregates&lt;&#x2F;strong&gt; is the compiler transformation that breaks an aggregate into independently optimized pieces.&lt;&#x2F;p&gt;
&lt;p&gt;LLVM documents the &lt;code&gt;sroa&lt;&#x2F;code&gt; pass as a transformation that breaks aggregate &lt;code&gt;alloca&lt;&#x2F;code&gt;s, such as local structs or arrays, into member-level &lt;code&gt;alloca&lt;&#x2F;code&gt;s and then, when possible, promotes those pieces to scalar SSA values. That last part is the money: once the fields are SSA values, the register allocator can keep them in registers, and later optimization passes can reason about each piece independently.&lt;&#x2F;p&gt;
&lt;p&gt;That phrasing matters. SROA is not magic fairy dust for any struct-shaped value anywhere in the program. It is strongest when the aggregate is local, non-escaping, and boring. If the compiler sees &quot;this local array is private to this function,&quot; it has a much easier proof than &quot;this pointer points to caller-owned memory and maybe something opaque can observe it.&quot;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;a-tiny-before-after&quot;&gt;A Tiny Before&#x2F;After&lt;&#x2F;h3&gt;
&lt;p&gt;Consider a function that updates a pair in place:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;c&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;struct&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; Pair {&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; long&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; a, b; };&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;long&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; sum_in_place&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;struct&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function z-definition z-parameters z-c&quot;&gt; Pair &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt;p) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    p&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-pointer-access z-c&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-member z-c&quot;&gt;a&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; +=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    p&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-pointer-access z-c&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-member z-c&quot;&gt;b&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; +=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 2&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; p&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-pointer-access z-c&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-member z-c&quot;&gt;a&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; +&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; p&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-pointer-access z-c&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-member z-c&quot;&gt;b&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The function must store the new &lt;code&gt;a&lt;&#x2F;code&gt; and &lt;code&gt;b&lt;&#x2F;code&gt; back through &lt;code&gt;p&lt;&#x2F;code&gt;, because mutating the caller&#x27;s object is part of its behavior. A compiler may still optimize the loads and arithmetic, but it cannot simply pretend the pointed-to object is a private local.&lt;&#x2F;p&gt;
&lt;p&gt;Now compare a by-value shape:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;c&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;long&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; sum_local&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;struct&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function z-definition z-parameters z-c z-variable z-punctuation&quot;&gt; Pair in) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;    long&lt;&#x2F;span&gt;&lt;span&gt; a &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; in&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-dot-access z-c&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-member z-c&quot;&gt;a&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; +&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;    long&lt;&#x2F;span&gt;&lt;span&gt; b &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; in&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-dot-access z-c&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-member z-c&quot;&gt;b&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; +&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 2&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span&gt; a &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;+&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; b;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;These functions are not semantically identical: the first updates the caller&#x27;s pair, the second does not. That difference is the point. In the second version the fields are just private values. On x86-64 SysV, a two-&lt;code&gt;long&lt;&#x2F;code&gt; struct is commonly passed in registers, and the body can collapse to something like:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;asm&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;lea&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-language&quot;&gt; rax&lt;&#x2F;span&gt;&lt;span&gt;, [&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-language&quot;&gt;rdi&lt;&#x2F;span&gt;&lt;span&gt; + &lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-language&quot;&gt;rsi&lt;&#x2F;span&gt;&lt;span&gt; + &lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;3&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;ret&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;No aggregate has to survive as memory in the optimized code. That is SROA&#x27;s happy path: aggregate-shaped source, scalar-shaped machine code.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;2-what-blocks-it-aliasing-and-visibility&quot;&gt;2. What Blocks It: Aliasing and Visibility&lt;&#x2F;h2&gt;
&lt;p&gt;The hard question for the optimizer is not &quot;would registers be faster?&quot; The answer is nearly always yes. The hard question is &quot;is it legal to keep this value in registers?&quot;&lt;&#x2F;p&gt;
&lt;p&gt;When code repeatedly mutates state through a pointer, the compiler has to preserve the observable behavior of that memory. This becomes especially restrictive around operations that may read or write memory in ways the optimizer cannot see through:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;calls that are not inlined,&lt;&#x2F;li&gt;
&lt;li&gt;volatile or atomic operations,&lt;&#x2F;li&gt;
&lt;li&gt;inline assembly with a &lt;code&gt;&quot;memory&quot;&lt;&#x2F;code&gt; clobber,&lt;&#x2F;li&gt;
&lt;li&gt;compiler or hardware fences,&lt;&#x2F;li&gt;
&lt;li&gt;raw pointers whose aliasing contract is weaker than a language-level unique reference.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Compilers can recover in many cases, especially after inlining or when they have strong &lt;code&gt;noalias&lt;&#x2F;code&gt; information. But the robust, easy-to-prove pattern is:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;copy aggregate from memory -&amp;gt; mutate private locals -&amp;gt; write aggregate back in one boundary phase&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Once the address of the local copy does not escape, the optimizer can scalarize the local copy freely. The pointer-owned memory is touched at the boundary. The hot loop works on private values.&lt;&#x2F;p&gt;
&lt;p&gt;That is the whole trick. You are not hand-allocating registers. You are making the compiler&#x27;s proof small enough that the normal optimization pipeline can do its job.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;3-minimal-reproduction-in-zig-and-rust&quot;&gt;3. Minimal Reproduction in Zig and Rust&lt;&#x2F;h2&gt;
&lt;p&gt;The examples below intentionally include a compiler-only memory barrier inside the loop. The barrier is not part of the TigerBeetle change; it is just a demonstration tool.&lt;&#x2F;p&gt;
&lt;p&gt;Why add it? Without a barrier, a sufficiently smart optimizer may hoist, sink, or combine memory operations in the small example, making the difference harder to see. A compiler barrier says: memory operations may not freely move across this point. It does not stop pure register arithmetic. That makes the contrast crisp:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;pointer version: every round touches memory, so the barrier pins the loads and stores in the loop;&lt;&#x2F;li&gt;
&lt;li&gt;local-copy version: the loop body touches private scalar values, so there is no per-round memory traffic to pin.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;In optimized output, especially for Rust&#x27;s &lt;code&gt;compiler_fence&lt;&#x2F;code&gt;, you may not see a barrier marker inside every local-copy iteration. Once the loop is register-only, the compiler-only barrier has no loop memory operations to constrain, so it may be moved relative to the register arithmetic. That is not a failure of the demo; the important thing to inspect is whether loads and stores remain in the loop body.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;zig&quot;&gt;Zig&lt;&#x2F;h3&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;zig&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;const&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; State&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; extern struct&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt;    blocks: [&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;8&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-type&quot;&gt;u128&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;};&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-comment&quot;&gt;&#x2F;&#x2F; Zig 0.15+ typed clobber syntax. This is a compiler barrier, not a CPU fence.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-storage z-type z-function&quot;&gt;inline fn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; compilerBarrier&lt;&#x2F;span&gt;&lt;span&gt;() &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-type&quot;&gt;void&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    asm volatile&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-variable&quot;&gt; ::: .{ .memory&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; true&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; });&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-function&quot;&gt;fn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; roundBad&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt;(state:&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; *&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;State) &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-type&quot;&gt;void&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-comment&quot;&gt;    &#x2F;&#x2F; BAD: mutate the aggregate through a pointer every iteration.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    var&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt; i:&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-type&quot;&gt; usize&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    while&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; (i&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; &amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 1000&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-variable&quot;&gt;) : (i&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; +=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span&gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt;        state.blocks[&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;] &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;+%=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt; state.blocks[&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;1&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt;        state.blocks[&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;] &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;^=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt; state.blocks[&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;2&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;        compilerBarrier&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;();&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-function&quot;&gt;fn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; roundGood&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt;(state:&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; *&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;State) &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-type&quot;&gt;void&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-comment&quot;&gt;    &#x2F;&#x2F; GOOD: copy by value into a private local array.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    var&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt; blocks: [&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;8&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-type&quot;&gt;u128&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt; state.blocks;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    var&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt; i:&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-type&quot;&gt; usize&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    while&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; (i&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; &amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 1000&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-variable&quot;&gt;) : (i&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; +=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span&gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;        blocks[&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;] &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;+%=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; blocks[&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;1&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;        blocks[&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;] &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;^=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; blocks[&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;2&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;        compilerBarrier&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;();&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt;    state.blocks&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt; blocks;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-storage z-type z-function&quot;&gt;export fn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; entry_bad&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt;(s:&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; *&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;State) &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-type&quot;&gt;void&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;    roundBad&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt;(s);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-storage z-type z-function&quot;&gt;export fn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; entry_good&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt;(s:&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; *&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;State) &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-type&quot;&gt;void&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;    roundGood&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt;(s);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The important line is:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;zig&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;var&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt; blocks: [&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;8&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-type&quot;&gt;u128&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt; state.blocks;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Read it literally:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;var&lt;&#x2F;code&gt; declares a mutable local.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;[8]u128&lt;&#x2F;code&gt; is a fixed-size array value, not a slice and not a pointer.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;= state.blocks&lt;&#x2F;code&gt; copies the array value out of the state.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Mutating &lt;code&gt;blocks&lt;&#x2F;code&gt; does not mutate &lt;code&gt;state.blocks&lt;&#x2F;code&gt; until the explicit assignment at the end. These shapes do not make a private copy:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;zig&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;const&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt; slice: []&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-type&quot;&gt;u128&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt; state.blocks[&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;0&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-variable&quot;&gt;..state.blocks.len];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;const&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt; p:&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; *&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;8&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-type&quot;&gt;u128&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; = &amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt;state.blocks;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Both are views of the original storage. They reintroduce the pointer-shaped memory access that we are trying to avoid.&lt;&#x2F;p&gt;
&lt;p&gt;Build and inspect:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;zig&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; build-obj sroa.zig -O ReleaseFast -fllvm -femit-asm=sroa.s&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;# Compare entry_bad and entry_good in sroa.s.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;# entry_bad should load&#x2F;store state in the loop.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;# entry_good should load before the loop and write back after the loop.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;For LLVM IR:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;zig&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; build-obj sroa.zig -O ReleaseFast -fllvm -femit-llvm-ir=sroa.ll&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h3 id=&quot;rust&quot;&gt;Rust&lt;&#x2F;h3&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;use&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-namespace&quot;&gt; core&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-namespace&quot;&gt;sync&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-namespace&quot;&gt;atomic&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;{compiler_fence,&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Ordering&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;};&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;#[&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-attribute z-rust&quot;&gt;repr&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;C&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;pub&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt; struct&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; State&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    blocks&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; [&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;u128&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 8&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;],&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;&#x2F;&#x2F;&#x2F; # Safety&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;&#x2F;&#x2F;&#x2F; `state` must be valid, properly aligned, and exclusively writable for the&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;&#x2F;&#x2F;&#x2F; duration of this call.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;&#x2F;&#x2F; SAFETY: the exported symbol name must be unique in the final linked program.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;#[&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;unsafe&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-attribute z-rust&quot;&gt;no_mangle&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;pub unsafe&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt; extern&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;C&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; fn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; entry_bad&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-variable&quot;&gt;(state&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-storage&quot;&gt;: *mut&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; State&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;    &#x2F;&#x2F; BAD: mutate through the raw pointer every iteration.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    for&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; _&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; in&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;..&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;1000&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        unsafe&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;            (&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt;state)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;blocks[&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;0&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; (&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt;state)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;blocks[&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;0&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;wrapping_add&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;((&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt;state)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;blocks[&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;1&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;]);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;            (&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt;state)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;blocks[&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;1&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; ^=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; (&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt;state)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;blocks[&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;2&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;        }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;        compiler_fence&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-namespace&quot;&gt;Ordering&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;SeqCst&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;&#x2F;&#x2F;&#x2F; # Safety&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&#x2F;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;&#x2F;&#x2F;&#x2F; `state` must be valid, properly aligned, and exclusively writable for the&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;&#x2F;&#x2F;&#x2F; duration of this call.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;&#x2F;&#x2F; SAFETY: the exported symbol name must be unique in the final linked program.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;#[&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;unsafe&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-attribute z-rust&quot;&gt;no_mangle&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;pub unsafe&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt; extern&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;C&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; fn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; entry_good&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-variable&quot;&gt;(state&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-storage&quot;&gt;: *mut&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; State&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;    &#x2F;&#x2F; GOOD: copy the array, work on the local copy, write back at the boundary.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust z-storage&quot;&gt;    let mut&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; blocks&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; unsafe&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; { (&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt;state)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;blocks };&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    for&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; _&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; in&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;..&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;1000&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt;        blocks[&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;0&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt; blocks[&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;0&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;wrapping_add&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-variable&quot;&gt;(blocks[&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;1&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;]);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt;        blocks[&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;1&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; ^=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt; blocks[&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;2&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;        compiler_fence&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-namespace&quot;&gt;Ordering&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;SeqCst&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    unsafe&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;        (&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt;state)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;blocks &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt; blocks;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;A few Rust details are deliberate:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;#[unsafe(no_mangle)]&lt;&#x2F;code&gt; is the Rust 2024-compatible spelling of &lt;code&gt;#[no_mangle]&lt;&#x2F;code&gt;; the safety obligation is that the exported symbol name does not collide in the final linked program.&lt;&#x2F;li&gt;
&lt;li&gt;The functions are &lt;code&gt;unsafe extern &quot;C&quot;&lt;&#x2F;code&gt; because a raw pointer from outside Rust needs a safety contract.&lt;&#x2F;li&gt;
&lt;li&gt;The unsafe blocks are small, which keeps the actual raw-pointer operations visible.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;[u128; 8]&lt;&#x2F;code&gt; is &lt;code&gt;Copy&lt;&#x2F;code&gt;, so &lt;code&gt;let mut blocks = (*state).blocks&lt;&#x2F;code&gt; is a by-value copy.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;compiler_fence&lt;&#x2F;code&gt; is compiler-only. It restricts memory reordering by the compiler but emits no hardware fence instruction of its own.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Build and inspect:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;rustc&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; sroa.rs&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-character z-escape&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;  --crate-type=lib&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-character z-escape&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;  --edition=2024&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-character z-escape&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;  -O&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-character z-escape&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;  -C target-cpu=native&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-character z-escape&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;  --emit=asm&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-character z-escape&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;  -o sroa.s&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;On a current optimized build, the pattern to look for is not &quot;zero memory instructions anywhere.&quot; The good version still has to load the initial state and store the final state. The key property is narrower and more important: the &lt;strong&gt;loop body&lt;&#x2F;strong&gt; should be register arithmetic, while the bad version should retain loads and stores in the loop.&lt;&#x2F;p&gt;
&lt;p&gt;For LLVM IR:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;rustc&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; sroa.rs&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-character z-escape&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;  --crate-type=lib&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-character z-escape&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;  --edition=2024&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-character z-escape&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;  -O&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-character z-escape&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;  -C target-cpu=native&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-character z-escape&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;  --emit=llvm-ir&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-character z-escape&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;  -o sroa.ll&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In the good function, you may still see setup and write-back memory operations. Inspect the loop itself. The updated lanes should appear as SSA values carried through the loop, not as repeated loads from and stores to &lt;code&gt;state&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;4-what-a-barrier-proves-in-the-demo&quot;&gt;4. What a Barrier Proves in the Demo&lt;&#x2F;h2&gt;
&lt;p&gt;A &lt;strong&gt;round&lt;&#x2F;strong&gt; is one repeatable step of an algorithm. In this toy example, a round is just:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;blocks[0] += blocks[1]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;blocks[1] ^= blocks[2]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In AEGIS-128L, the state update is built from AES round-function calls plus XORs with message blocks. The exact operation is different, but the code-generation problem is the same: several 128-bit lanes are updated repeatedly, and the fast path wants those lanes in registers.&lt;&#x2F;p&gt;
&lt;p&gt;The toy round only touches three lanes so the assembly stays easy to read. The TigerBeetle update is denser: it rotates through all eight lanes and then mixes in the two message blocks. That makes the register-residency problem more important, not less.&lt;&#x2F;p&gt;
&lt;p&gt;The demo barrier is a compiler barrier for memory operations:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Rust&#x27;s &lt;code&gt;compiler_fence&lt;&#x2F;code&gt; emits no machine instruction, but it restricts compiler memory reordering.&lt;&#x2F;li&gt;
&lt;li&gt;Zig&#x27;s empty &lt;code&gt;asm volatile&lt;&#x2F;code&gt; with a &lt;code&gt;.memory&lt;&#x2F;code&gt; clobber tells the optimizer that the assembly may touch arbitrary undeclared memory.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Neither one is a general inter-thread synchronization recipe. If you need synchronization, use the language&#x27;s atomic operations and fences correctly. Here the barrier is only there to make the optimizer&#x27;s memory behavior easy to see.&lt;&#x2F;p&gt;
&lt;p&gt;The asymmetry is the useful part:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Through pointer: load -&amp;gt; math -&amp;gt; store -&amp;gt; barrier, repeated each round&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Local copy:      load once -&amp;gt; register math loop -&amp;gt; boundary write-back phase&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;That is why the local-copy version can be much faster even though it appears to do an extra copy at the start. It pays a small boundary cost to avoid a large per-round cost. At the source level the boundary is one assignment, although the backend may lower that assignment to several scalar or vector stores.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;5-back-to-tigerbeetle-aegis-128l&quot;&gt;5. Back to TigerBeetle: AEGIS-128L&lt;&#x2F;h2&gt;
&lt;p&gt;The actual TigerBeetle PR is tiny. In &lt;code&gt;State128L.update&lt;&#x2F;code&gt;, the old code took a pointer to the state lanes:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;zig&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;const&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; blocks&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; = &amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt;state.blocks;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The new code copies the lanes into a local array:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;zig&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;comptime&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; assert&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt;(state.blocks.len&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; ==&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 8&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;var&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt; blocks: [&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;8&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;]AesBlock&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt; state.blocks;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;const&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; tmp&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; blocks[&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;7&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;inline for&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; ([_]&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-type&quot;&gt;usize&lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;7&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 6&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 5&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 4&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 3&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 2&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span&gt; }) &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;|&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;i&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;|&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    blocks[i] &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; blocks[i&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;].&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;encrypt&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt;(blocks[i]);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;blocks[&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;] &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt; tmp.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;encrypt&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;(blocks[&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;0&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;]);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;blocks[&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;] &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; blocks[&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;0&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;].&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;xorBlocks&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt;(d1);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;blocks[&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;4&lt;&#x2F;span&gt;&lt;span&gt;] &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; blocks[&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;4&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;].&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;xorBlocks&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt;(d2);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt;state.blocks&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt; blocks;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The &lt;code&gt;inline while&lt;&#x2F;code&gt; to &lt;code&gt;inline for&lt;&#x2F;code&gt; cleanup is nice, but the important performance change is pointer-to-array -&amp;gt; array value. That gives LLVM a private aggregate to scalarize. The final assignment is one source-level write-back phase after the update; the backend may still emit several stores to materialize the array.&lt;&#x2F;p&gt;
&lt;p&gt;Why does AEGIS respond so strongly? AEGIS-128L has a 1024-bit state made of eight 128-bit AES blocks, and the family is constructed from the AES encryption round function. On CPUs with AES instructions, the round operation wants to live in vector registers:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;x86 has &lt;code&gt;AESENC&lt;&#x2F;code&gt; and &lt;code&gt;AESENCLAST&lt;&#x2F;code&gt;, plus vector forms such as &lt;code&gt;VAESENC&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;ARM has AES instructions such as &lt;code&gt;AESE&lt;&#x2F;code&gt; and &lt;code&gt;AESMC&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Those instructions consume register operands and produce register results. If the surrounding code bounces every lane through memory around the AES instruction, throughput suffers. If the lanes stay in registers, the CPU can keep more AES operations in flight and avoid repeated load&#x2F;store traffic.&lt;&#x2F;p&gt;
&lt;p&gt;The PR body shows exactly that contrast. The old assembly repeatedly loaded two lanes, ran &lt;code&gt;vaesenc&lt;&#x2F;code&gt;, and stored the result. The new assembly had several &lt;code&gt;vaesenc&lt;&#x2F;code&gt; instructions running on registers before the final stores. The reported microbenchmark improved from &lt;code&gt;aegis-old&lt;&#x2F;code&gt; wall time &lt;code&gt;0.10&lt;&#x2F;code&gt; to &lt;code&gt;aegis-new&lt;&#x2F;code&gt; wall time &lt;code&gt;0.04&lt;&#x2F;code&gt;, with lower instruction count and higher IPC. The reported end-to-end benchmark moved the &lt;code&gt;i8g&lt;&#x2F;code&gt; throughput metric from &lt;code&gt;359,253&lt;&#x2F;code&gt; to &lt;code&gt;387,293&lt;&#x2F;code&gt; and &lt;code&gt;i4i&lt;&#x2F;code&gt; from &lt;code&gt;263,106&lt;&#x2F;code&gt; to &lt;code&gt;271,259&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;That is a real systems-performance lesson: a local code-shape change in a crypto inner loop can move whole-application throughput when that loop is on the critical path.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;6-how-far-this-generalizes&quot;&gt;6. How Far This Generalizes&lt;&#x2F;h2&gt;
&lt;p&gt;The pattern is broadly useful, but it is not a law of nature. Use it when the hot state is small enough and plain enough that copying it locally lets the optimizer remove repeated memory traffic.&lt;&#x2F;p&gt;
&lt;p&gt;Good candidates:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;fixed-size arrays of integers or SIMD&#x2F;vector blocks,&lt;&#x2F;li&gt;
&lt;li&gt;small POD-style structs,&lt;&#x2F;li&gt;
&lt;li&gt;cipher or hash states,&lt;&#x2F;li&gt;
&lt;li&gt;parser states,&lt;&#x2F;li&gt;
&lt;li&gt;tight loops where the same fields are updated repeatedly.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Important caveats:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Register pressure still exists.&lt;&#x2F;strong&gt; If the local state is larger than the available register file, the allocator will spill some pieces. That can still be better than pointer traffic every round, but it is not free.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Copy cost can dominate.&lt;&#x2F;strong&gt; If you only touch one field once, copying the whole aggregate is probably the wrong move.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Observable memory is different.&lt;&#x2F;strong&gt; Do not apply this blindly to volatile memory, memory-mapped IO, atomics, or data that must be observed between steps.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Rust ownership matters.&lt;&#x2F;strong&gt; &lt;code&gt;Copy&lt;&#x2F;code&gt; arrays are ideal. Moving non-&lt;code&gt;Copy&lt;&#x2F;code&gt; values into locals may be possible, but types with &lt;code&gt;Drop&lt;&#x2F;code&gt;, borrowing invariants, or interior aliasing often make the optimization less clean.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Measure the loop.&lt;&#x2F;strong&gt; The goal is not to see no memory instructions anywhere. The goal is to remove unnecessary memory traffic from the hot loop.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;If the state is slightly too large, tile it: copy the hot subset into locals, work on that subset, write it back, then move to the next subset. Shorter live ranges often help the register allocator more than one giant local copy.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;7-takeaways&quot;&gt;7. Takeaways&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;SROA is the compiler&#x27;s ability to split local aggregate storage into scalar SSA values.&lt;&#x2F;li&gt;
&lt;li&gt;Pointer-shaped code can hide whether memory is private, especially around opaque memory effects.&lt;&#x2F;li&gt;
&lt;li&gt;Copying a small aggregate into a local value gives the optimizer a private object it can scalarize.&lt;&#x2F;li&gt;
&lt;li&gt;In hot loops, the win is not the copy itself. The win is avoiding repeated &lt;code&gt;load -&amp;gt; operate -&amp;gt; store&lt;&#x2F;code&gt; traffic.&lt;&#x2F;li&gt;
&lt;li&gt;In Rust, prefer safe &lt;code&gt;&amp;amp;mut&lt;&#x2F;code&gt; APIs in ordinary code, and use raw pointers only at real FFI or low-level boundaries with explicit safety contracts.&lt;&#x2F;li&gt;
&lt;li&gt;In Zig, remember that &lt;code&gt;[N]T&lt;&#x2F;code&gt; assignment copies the array value; slices and pointers keep referring to the original storage.&lt;&#x2F;li&gt;
&lt;li&gt;Always verify with optimized assembly or LLVM IR.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The deeper habit is simple: write the code so the optimizer can prove the thing you already know. &quot;Copy to locals, work privately, write back at the boundary&quot; is one of the most useful ways to make that proof obvious.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;references&quot;&gt;References&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tigerbeetle&#x2F;tigerbeetle&#x2F;pull&#x2F;3201&quot;&gt;TigerBeetle PR #3201: Improve AEGIS codegen&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tigerbeetle&#x2F;tigerbeetle&#x2F;pull&#x2F;3201#issuecomment-3219968770&quot;&gt;TigerBeetle PR #3201 discussion: matklad on SROA&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;llvm.org&#x2F;docs&#x2F;Passes.html#sroa-scalar-replacement-of-aggregates&quot;&gt;LLVM &lt;code&gt;sroa&lt;&#x2F;code&gt;: Scalar Replacement of Aggregates&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;std&#x2F;sync&#x2F;atomic&#x2F;fn.compiler_fence.html&quot;&gt;Rust &lt;code&gt;compiler_fence&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;edition-guide&#x2F;rust-2024&#x2F;unsafe-attributes.html&quot;&gt;Rust 2024 unsafe attributes&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;ziglang.org&#x2F;documentation&#x2F;master&#x2F;#Assembly&quot;&gt;Zig inline assembly and clobbers&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;datatracker.ietf.org&#x2F;doc&#x2F;draft-irtf-cfrg-aegis-aead&#x2F;&quot;&gt;The AEGIS Family of Authenticated Encryption Algorithms, IETF draft&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;csrc.nist.gov&#x2F;pubs&#x2F;fips&#x2F;197&#x2F;final&quot;&gt;NIST FIPS 197: Advanced Encryption Standard&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.intel.com&#x2F;content&#x2F;www&#x2F;us&#x2F;en&#x2F;developer&#x2F;articles&#x2F;technical&#x2F;advanced-encryption-standard-instructions-aes-ni.html&quot;&gt;Intel AES-NI overview&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;godbolt.org&quot;&gt;Compiler Explorer&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</description>
      </item>
      <item>
          <title>Why x86 Zeroes a Register With `xor eax, eax`</title>
          <pubDate>Mon, 08 Jun 2026 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://debasishg.github.io/blog/xor-x86/</link>
          <guid>https://debasishg.github.io/blog/xor-x86/</guid>
          <description xml:base="https://debasishg.github.io/blog/xor-x86/">&lt;h1 id=&quot;why-x86-zeroes-a-register-with-xor-eax-eax&quot;&gt;Why x86 Zeroes a Register With &lt;code&gt;xor eax, eax&lt;&#x2F;code&gt;&lt;&#x2F;h1&gt;
&lt;p&gt;If you have ever disassembled a program - even a trivial one - you have almost certainly seen this line:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;asm&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;xor&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-language&quot;&gt; eax&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-language&quot;&gt;eax&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;It looks like a riddle. The instruction says &quot;XOR the &lt;code&gt;eax&lt;&#x2F;code&gt; register with itself,&quot; but what it actually &lt;em&gt;means&lt;&#x2F;em&gt; is &quot;set &lt;code&gt;eax&lt;&#x2F;code&gt; to zero.&quot; In optimized code, compilers and assembly programmers commonly reach for it instead of the more obvious:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;asm&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;mov&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-language&quot;&gt; eax&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Both leave &lt;code&gt;eax&lt;&#x2F;code&gt; holding zero. So why does the industry so often prefer the cryptic one? The short answer: it is smaller, it avoids a false dependency, and many CPUs have been taught to recognize it as a special &quot;I want a zero&quot; signal. The longer answer touches instruction encoding, out-of-order execution, register renaming, and one subtle correctness trap involving CPU flags.&lt;&#x2F;p&gt;
&lt;p&gt;This post works through all of it - starting from the intuition and ending at the details an assembly author actually needs to get right.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;the-essence-for-the-impatient&quot;&gt;The Essence (for the impatient)&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;xor eax, eax&lt;&#x2F;code&gt;&lt;&#x2F;strong&gt; and &lt;strong&gt;&lt;code&gt;mov eax, 0&lt;&#x2F;code&gt;&lt;&#x2F;strong&gt; both &lt;strong&gt;set &lt;code&gt;eax&lt;&#x2F;code&gt; to 0.&lt;&#x2F;strong&gt; Their effect on the &lt;em&gt;register value&lt;&#x2F;em&gt; is identical.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;xor&lt;&#x2F;code&gt; wins on &lt;strong&gt;size&lt;&#x2F;strong&gt; (2 bytes vs. 5 for &lt;code&gt;eax&lt;&#x2F;code&gt;) and &lt;strong&gt;dependency behavior&lt;&#x2F;strong&gt; (modern CPUs recognize it as a &quot;zeroing idiom&quot; that does not need the old register value).&lt;&#x2F;li&gt;
&lt;li&gt;They are &lt;strong&gt;not&lt;&#x2F;strong&gt; fully interchangeable: &lt;code&gt;xor&lt;&#x2F;code&gt; &lt;em&gt;modifies the CPU flags&lt;&#x2F;em&gt;; &lt;code&gt;mov&lt;&#x2F;code&gt; leaves them untouched. In flag-sensitive code, this difference matters.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;If you only remember one thing: prefer &lt;code&gt;xor reg, reg&lt;&#x2F;code&gt; to zero a general-purpose register, &lt;em&gt;unless&lt;&#x2F;em&gt; you need the surrounding flags preserved.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;why-xor-with-itself-equals-zero&quot;&gt;Why XOR-with-itself Equals Zero&lt;&#x2F;h2&gt;
&lt;p&gt;XOR (exclusive OR) is a bitwise operation that compares two bits and returns &lt;code&gt;1&lt;&#x2F;code&gt; only when the bits differ:&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;a&lt;&#x2F;th&gt;&lt;th&gt;b&lt;&#x2F;th&gt;&lt;th&gt;a XOR b&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;0&lt;&#x2F;td&gt;&lt;td&gt;0&lt;&#x2F;td&gt;&lt;td&gt;0&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;0&lt;&#x2F;td&gt;&lt;td&gt;1&lt;&#x2F;td&gt;&lt;td&gt;1&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;1&lt;&#x2F;td&gt;&lt;td&gt;0&lt;&#x2F;td&gt;&lt;td&gt;1&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;1&lt;&#x2F;td&gt;&lt;td&gt;1&lt;&#x2F;td&gt;&lt;td&gt;0&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;Now apply that to a value XOR&#x27;d with &lt;em&gt;itself&lt;&#x2F;em&gt;. Every bit is being compared to a copy of itself, so every pair of bits is identical - and identical bits always produce &lt;code&gt;0&lt;&#x2F;code&gt;. Whatever was in &lt;code&gt;eax&lt;&#x2F;code&gt;, &lt;code&gt;eax XOR eax&lt;&#x2F;code&gt; is guaranteed to be all-zero bits. That is the trick: you do not need to know the old value to wipe it out.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;reason-1-smaller-code&quot;&gt;Reason 1: Smaller Code&lt;&#x2F;h2&gt;
&lt;p&gt;Machine instructions are bytes, and bytes have costs. Here is how the two encode for the &lt;code&gt;eax&lt;&#x2F;code&gt; case:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;xor eax, eax   →  31 C0              (2 bytes)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;mov eax, 0     →  B8 00 00 00 00     (5 bytes)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The usual shortest encoding of &lt;code&gt;mov eax, 0&lt;&#x2F;code&gt; has to carry the literal value &lt;code&gt;0&lt;&#x2F;code&gt; as a full 32-bit immediate - four bytes of zeros riding along just to say &quot;nothing.&quot; &lt;code&gt;xor eax, eax&lt;&#x2F;code&gt; encodes the operation alone; the operands are registers, so there is no immediate to store.&lt;&#x2F;p&gt;
&lt;p&gt;Small x86-64 note: writing a 32-bit register such as &lt;code&gt;eax&lt;&#x2F;code&gt; also clears the upper half of the corresponding 64-bit register, &lt;code&gt;rax&lt;&#x2F;code&gt;. So &lt;code&gt;xor eax, eax&lt;&#x2F;code&gt; is also a common way to zero all of &lt;code&gt;rax&lt;&#x2F;code&gt;; writing &lt;code&gt;xor rax, rax&lt;&#x2F;code&gt; works too, but needs an extra REX prefix byte.&lt;&#x2F;p&gt;
&lt;p&gt;Three extra bytes per zeroing sounds trivial. But zeroing registers is one of the most common things code does (loop counters, return values, clearing accumulators). Multiply three bytes across a large binary and you affect &lt;strong&gt;code density&lt;&#x2F;strong&gt; - how much real work fits in the instruction cache. Tighter code means fewer cache misses, which on a modern CPU is a real performance lever, not a micro-optimization fetish.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;reason-2-the-cpu-knows-this-trick&quot;&gt;Reason 2: The CPU Knows This Trick&lt;&#x2F;h2&gt;
&lt;p&gt;Here is where it gets interesting. On modern x86 processors, &lt;code&gt;xor eax, eax&lt;&#x2F;code&gt; is not treated as a generic XOR that happens to produce zero. The hardware specifically recognizes the pattern &lt;code&gt;xor reg, reg&lt;&#x2F;code&gt; as a &lt;strong&gt;zeroing idiom&lt;&#x2F;strong&gt; and gives it special treatment.&lt;&#x2F;p&gt;
&lt;p&gt;To understand why that helps, you need two ideas from how modern CPUs actually run.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;out-of-order-execution-and-dependencies&quot;&gt;Out-of-order execution and dependencies&lt;&#x2F;h3&gt;
&lt;p&gt;Modern CPUs do not execute instructions strictly one after another. They run many in flight at once, reordering them to keep the execution units busy. The constraint is &lt;strong&gt;data dependencies&lt;&#x2F;strong&gt;: if instruction B needs the result of instruction A, B has to wait for A.&lt;&#x2F;p&gt;
&lt;p&gt;A normal XOR reads both its operands. So in principle &lt;code&gt;xor eax, eax&lt;&#x2F;code&gt; &lt;em&gt;reads&lt;&#x2F;em&gt; the old &lt;code&gt;eax&lt;&#x2F;code&gt; before overwriting it - which would force it to wait for whatever last wrote &lt;code&gt;eax&lt;&#x2F;code&gt;. That is a false dependency: the result (zero) does not actually depend on the old value at all.&lt;&#x2F;p&gt;
&lt;p&gt;CPUs are smart enough to know this. When they see &lt;code&gt;xor reg, reg&lt;&#x2F;code&gt;, they recognize the result is unconditionally zero and &lt;strong&gt;break the dependency chain&lt;&#x2F;strong&gt; - the instruction does not wait on the previous &lt;code&gt;eax&lt;&#x2F;code&gt; writer. This frees the out-of-order engine to schedule it immediately, which is exactly what you want in a tight, performance-critical loop.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;register-renaming-zeroing-for-free&quot;&gt;Register renaming - zeroing for &quot;free&quot;&lt;&#x2F;h3&gt;
&lt;p&gt;There is a second, deeper trick. Internally, the named registers you write (&lt;code&gt;eax&lt;&#x2F;code&gt;, &lt;code&gt;ebx&lt;&#x2F;code&gt;, …) are mapped onto a much larger pool of physical registers - a mechanism called &lt;strong&gt;register renaming&lt;&#x2F;strong&gt;. It is how the CPU runs many instructions concurrently without them stepping on each other&#x27;s register names.&lt;&#x2F;p&gt;
&lt;p&gt;On many microarchitectures, a recognized zeroing idiom can be handled entirely in or near this renaming stage: the CPU allocates a fresh physical register whose value is known to be zero. No integer ALU has to run, no arithmetic happens, and in the best case the instruction has &lt;strong&gt;zero execution latency&lt;&#x2F;strong&gt;. It is not literally free - it still takes bytes in the instruction stream and uses front-end&#x2F;retirement resources - but it can disappear from the execution units. A &lt;code&gt;mov eax, 0&lt;&#x2F;code&gt; is still cheap, but it is not the canonical dependency-breaking zero idiom and generally has to create an immediate value through the normal instruction path.&lt;&#x2F;p&gt;
&lt;p&gt;So &lt;code&gt;xor eax, eax&lt;&#x2F;code&gt; is not just smaller - the processor can sometimes make it &lt;em&gt;vanish&lt;&#x2F;em&gt; from the execution part of the pipeline.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;reason-3-convention-and-history&quot;&gt;Reason 3: Convention and History&lt;&#x2F;h2&gt;
&lt;p&gt;The idiom is old. Early x86 processors already benefited from the smaller encoding, so assembly programmers and compiler authors adopted &lt;code&gt;xor reg, reg&lt;&#x2F;code&gt; as the standard way to zero a register when flags did not need to be preserved. Hardware designers, in turn, optimized for the pattern they saw everywhere - a feedback loop that cemented it as &lt;em&gt;the&lt;&#x2F;em&gt; idiom. Today GCC, Clang, MSVC, and hand-written assembly all commonly use it in that role.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;the-catch-flags-are-not-the-same&quot;&gt;The Catch: Flags Are Not the Same&lt;&#x2F;h2&gt;
&lt;p&gt;Now the nuance that trips people up - and the reason &quot;functionally equivalent&quot; is an overstatement.&lt;&#x2F;p&gt;
&lt;p&gt;x86 has a set of status &lt;strong&gt;flags&lt;&#x2F;strong&gt; (EFLAGS in 32-bit mode, RFLAGS in 64-bit mode) that many instructions update as a side effect. They record things about a result: was it zero, was it negative, did it carry, and so on. Conditional jumps like &lt;code&gt;jz&lt;&#x2F;code&gt; (jump if zero) read these flags.&lt;&#x2F;p&gt;
&lt;p&gt;The crucial difference:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;mov eax, 0&lt;&#x2F;code&gt; touches no flags.&lt;&#x2F;strong&gt; A &lt;code&gt;mov&lt;&#x2F;code&gt; only moves data. Every flag keeps whatever value it had before.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;xor eax, eax&lt;&#x2F;code&gt; rewrites the flags&lt;&#x2F;strong&gt; based on its result (zero):&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Flag&lt;&#x2F;th&gt;&lt;th&gt;Meaning&lt;&#x2F;th&gt;&lt;th&gt;After &lt;code&gt;xor eax, eax&lt;&#x2F;code&gt;&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;ZF (Zero)&lt;&#x2F;td&gt;&lt;td&gt;result was zero&lt;&#x2F;td&gt;&lt;td&gt;&lt;strong&gt;1&lt;&#x2F;strong&gt; (set)&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;SF (Sign)&lt;&#x2F;td&gt;&lt;td&gt;result was negative&lt;&#x2F;td&gt;&lt;td&gt;0&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;PF (Parity)&lt;&#x2F;td&gt;&lt;td&gt;low byte has even parity&lt;&#x2F;td&gt;&lt;td&gt;1 (zero has even parity)&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;CF (Carry)&lt;&#x2F;td&gt;&lt;td&gt;unsigned carry&#x2F;borrow&lt;&#x2F;td&gt;&lt;td&gt;0&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;OF (Overflow)&lt;&#x2F;td&gt;&lt;td&gt;signed overflow&lt;&#x2F;td&gt;&lt;td&gt;0&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;AF (Adjust)&lt;&#x2F;td&gt;&lt;td&gt;auxiliary carry&lt;&#x2F;td&gt;&lt;td&gt;undefined&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;So the two instructions leave the &lt;em&gt;register&lt;&#x2F;em&gt; identical but the &lt;em&gt;flags&lt;&#x2F;em&gt; in completely different states.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;when-this-bites&quot;&gt;When this bites&lt;&#x2F;h3&gt;
&lt;p&gt;Consider code that sets up a flag, then zeroes a register, then branches on that flag:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;asm&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-comment&quot;&gt;; ... something earlier sets ZF based on a comparison ...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;mov&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-language&quot;&gt; eax&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;0&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt;    ; eax = 0, ZF is preserved from the comparison&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;jz&lt;&#x2F;span&gt;&lt;span&gt;  label     &lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt;; branches on the EARLIER comparison&amp;#39;s result&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Swap in &lt;code&gt;xor&lt;&#x2F;code&gt; and the meaning changes:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;asm&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-comment&quot;&gt;; ... something earlier sets ZF based on a comparison ...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;xor&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-language&quot;&gt; eax&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-language&quot;&gt;eax&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt;  ; eax = 0, but ZF is now forced to 1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;jz&lt;&#x2F;span&gt;&lt;span&gt;  label     &lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt;; ALWAYS branches - the comparison&amp;#39;s result was clobbered&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The &lt;code&gt;xor&lt;&#x2F;code&gt; version destroyed the flag the &lt;code&gt;jz&lt;&#x2F;code&gt; depended on. This is a genuine correctness bug, not a style preference.&lt;&#x2F;p&gt;
&lt;p&gt;Conversely, sometimes the flag side effect is &lt;em&gt;useful&lt;&#x2F;em&gt; - if you wanted ZF set anyway, &lt;code&gt;xor&lt;&#x2F;code&gt; gives you the zero and the flag in one 2-byte instruction.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;so-which-should-you-use&quot;&gt;So Which Should You Use?&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;strong&gt;Reach for &lt;code&gt;xor reg, reg&lt;&#x2F;code&gt;&lt;&#x2F;strong&gt; in the overwhelmingly common case: you want a zero, and you do not need any flags preserved across the zeroing. You get the smallest encoding and the dependency-breaking form compilers generally prefer.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Reach for &lt;code&gt;mov reg, 0&lt;&#x2F;code&gt;&lt;&#x2F;strong&gt; when surrounding code depends on the flags as they were &lt;em&gt;before&lt;&#x2F;em&gt; the zeroing - you are deliberately interleaving a register clear into a sequence that will branch on an earlier comparison. Here you pay extra bytes and a possible execution cost to keep the flags pristine.&lt;&#x2F;p&gt;
&lt;p&gt;A practical heuristic: in straight-line code where the zeroing is followed by a fresh comparison anyway, the flag difference is invisible, so &lt;code&gt;xor&lt;&#x2F;code&gt; is free and correct. The flags only matter when a &lt;em&gt;later&lt;&#x2F;em&gt; instruction reads a flag set by an &lt;em&gt;earlier&lt;&#x2F;em&gt; one, with the zeroing wedged in between.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;a-more-accurate-one-liner&quot;&gt;A More Accurate One-Liner&lt;&#x2F;h2&gt;
&lt;p&gt;The tempting summary - &quot;&lt;code&gt;xor eax, eax&lt;&#x2F;code&gt; and &lt;code&gt;mov eax, 0&lt;&#x2F;code&gt; are equivalent&quot; - is wrong in one important way. A precise version:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;mov eax, 0&lt;&#x2F;code&gt; and &lt;code&gt;xor eax, eax&lt;&#x2F;code&gt; both set &lt;code&gt;eax&lt;&#x2F;code&gt; to zero, but they are not fully equivalent: &lt;code&gt;xor&lt;&#x2F;code&gt; sets ZF, clears CF and OF, sets SF and PF from the result, and leaves AF undefined, while &lt;code&gt;mov&lt;&#x2F;code&gt; leaves all flags unchanged. In nearly all cases &lt;code&gt;xor eax, eax&lt;&#x2F;code&gt; is preferred for its compactness (2 bytes vs. 5) and dependency-breaking zero-idiom recognition - unless preserving the existing flags is required.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;takeaways&quot;&gt;Takeaways&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Value:&lt;&#x2F;strong&gt; identical - both produce zero.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Size:&lt;&#x2F;strong&gt; &lt;code&gt;xor&lt;&#x2F;code&gt; is 2 bytes, &lt;code&gt;mov&lt;&#x2F;code&gt; is 5. Better code density, better instruction-cache behavior.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Speed:&lt;&#x2F;strong&gt; the CPU recognizes &lt;code&gt;xor reg, reg&lt;&#x2F;code&gt; as a zeroing idiom - it breaks the false dependency and can resolve during register renaming on many cores, sometimes with zero execution latency.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Flags:&lt;&#x2F;strong&gt; the real asymmetry. &lt;code&gt;xor&lt;&#x2F;code&gt; rewrites most relevant status flags and leaves AF undefined; &lt;code&gt;mov&lt;&#x2F;code&gt; preserves them. This is architectural, not a microarchitecture-specific optimization, and it is the one place the choice can change program behavior.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Rule of thumb:&lt;&#x2F;strong&gt; zero with &lt;code&gt;xor&lt;&#x2F;code&gt; by default; switch to &lt;code&gt;mov&lt;&#x2F;code&gt; only when flags must survive the zeroing.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;&lt;em&gt;Inspired by &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;youtu.be&#x2F;hgcNM-6wr34?si=j2gKGnJjOOa2NC0f&quot;&gt;x86 Internals for Fun &amp;amp; Profit • Matt Godbolt • GOTO 2014&lt;&#x2F;a&gt;.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Set-Associative Caches: Trading Global Optimality for Predictable Speed</title>
          <pubDate>Mon, 01 Jun 2026 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://debasishg.github.io/blog/set-associative-cache/</link>
          <guid>https://debasishg.github.io/blog/set-associative-cache/</guid>
          <description xml:base="https://debasishg.github.io/blog/set-associative-cache/">&lt;h1 id=&quot;set-associative-caches-trading-global-optimality-for-predictable-speed&quot;&gt;Set-Associative Caches: Trading Global Optimality for Predictable Speed&lt;&#x2F;h1&gt;
&lt;p&gt;Caches are everywhere in modern systems: CPU L1&#x2F;L2 caches, database buffer caches, storage-engine object caches, and application-level LRU maps. They do not all make the same trade-offs. A set-associative cache sits at a particular point in the design space: it gives up global replacement freedom in exchange for bounded probing, compact layout, and predictable cache-line-friendly access.&lt;&#x2F;p&gt;
&lt;p&gt;This post walks through what that means, why TigerBeetle uses a set-associative tier inside its &lt;code&gt;CacheMap&lt;&#x2F;code&gt;, and how a focused optimization to its SIMD search path improved instruction-level parallelism.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;1-what-is-a-set-associative-cache&quot;&gt;1. What Is a Set-Associative Cache?&lt;&#x2F;h2&gt;
&lt;p&gt;A &lt;strong&gt;set-associative cache&lt;&#x2F;strong&gt; is a caching strategy borrowed from CPU architecture. It sits between two extremes:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;A &lt;strong&gt;fully associative cache&lt;&#x2F;strong&gt; lets an item live anywhere in the cache.&lt;&#x2F;li&gt;
&lt;li&gt;A &lt;strong&gt;direct-mapped cache&lt;&#x2F;strong&gt; maps each item to exactly one slot.&lt;&#x2F;li&gt;
&lt;li&gt;A &lt;strong&gt;set-associative cache&lt;&#x2F;strong&gt; divides the cache into &lt;strong&gt;sets&lt;&#x2F;strong&gt;, each containing a small fixed number of &lt;strong&gt;ways&lt;&#x2F;strong&gt; or &lt;strong&gt;slots&lt;&#x2F;strong&gt;. That fixed number is the cache&#x27;s &lt;strong&gt;associativity&lt;&#x2F;strong&gt;.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The hash of a key determines which set the item belongs to. Within that set, the item may occupy any way. When the set is full, a local replacement policy chooses a way to evict.&lt;&#x2F;p&gt;
&lt;p&gt;For example, a 4-way set-associative cache with 256 sets has 1024 total slots.  Every key hashes to one of the 256 sets and can occupy any of that set&#x27;s 4 slots. When the set overflows, one of those 4 entries is evicted.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;benefits&quot;&gt;Benefits&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Predictable lookup cost.&lt;&#x2F;strong&gt; A lookup checks a small fixed number of slots.  There are no long collision chains or unbounded probe tails.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Fewer conflict misses than direct mapping.&lt;&#x2F;strong&gt; Multiple keys that hash to the same set can still coexist.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Compact memory layout.&lt;&#x2F;strong&gt; Sets and their metadata can be packed tightly, which matters in low-level hot paths.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;trade-off&quot;&gt;Trade-Off&lt;&#x2F;h3&gt;
&lt;p&gt;Unlike a fully associative cache, an item cannot live anywhere. If one set is hot and overflows, the cache evicts aggressively from that set even if colder entries exist elsewhere.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;2-how-it-differs-from-an-ordinary-software-cache&quot;&gt;2. How It Differs from an Ordinary Software Cache&lt;&#x2F;h2&gt;
&lt;p&gt;A typical general-purpose software cache, such as a &lt;code&gt;HashMap&lt;&#x2F;code&gt; plus a global LRU list, behaves differently:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Items can be placed anywhere in the structure.&lt;&#x2F;li&gt;
&lt;li&gt;Lookups are usually average-case &lt;code&gt;O(1)&lt;&#x2F;code&gt; through the hash table.&lt;&#x2F;li&gt;
&lt;li&gt;Eviction is global: the victim is chosen from the whole cache, not just one small set.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;A set-associative cache, by contrast:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Restricts a key&#x27;s possible locations to one set.&lt;&#x2F;li&gt;
&lt;li&gt;Evicts locally from that set.&lt;&#x2F;li&gt;
&lt;li&gt;Usually has a fixed capacity and a small fixed associativity.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;This gives up some hit-ratio flexibility. You may evict a warm item from an overfull set while colder items sit untouched elsewhere. The payoff is a simple, bounded, CPU-friendly memory access pattern.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;3-memory-layout-hash-map-vs-set-associative-cache&quot;&gt;3. Memory Layout: Hash Map vs. Set-Associative Cache&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;a-pointer-heavy-chained-hash-map&quot;&gt;A Pointer-Heavy Chained Hash Map&lt;&#x2F;h3&gt;
&lt;p&gt;One classic hash-table layout uses separate chaining:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[Bucket 0] -&amp;gt; pointer -&amp;gt; [Node(key, value, next)]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[Bucket 1] -&amp;gt; pointer -&amp;gt; [Node(key, value, next)]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[Bucket 2] -&amp;gt; pointer -&amp;gt; [Node(key, value, next)]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In that layout:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Nodes are scattered across the heap.&lt;&#x2F;li&gt;
&lt;li&gt;Traversal requires repeated pointer dereferences.&lt;&#x2F;li&gt;
&lt;li&gt;Spatial locality is poor, because adjacent buckets do not imply adjacent keys or values.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;An illustrative lookup looks like this:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;index = hash(key) % bucket_count&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;node = buckets[index]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;while node != null:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    if node.key == key:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        return node.value&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    node = node.next&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;return null&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This is deliberately pseudocode. Labeling it as Rust would be misleading:
idiomatic Rust code would call &lt;code&gt;map.get(key)&lt;&#x2F;code&gt;, and Rust&#x27;s &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;std&#x2F;collections&#x2F;struct.HashMap.html&quot;&gt;&lt;code&gt;std::collections::HashMap&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; is an open-addressed table rather than a chained node table. The sketch is here only to show the memory-access pattern of a pointer-heavy hash table.&lt;&#x2F;p&gt;
&lt;p&gt;Modern hash maps are often much more cache-conscious than this. SwissTable-like metadata groups, Robin Hood probing, and contiguous slot arrays all improve locality. So the comparison is not &quot;set-associative cache versus every hash map.&quot; It is &quot;bounded set scan versus a layout that may chase pointers or probe an unbounded number of locations.&quot;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;set-associative-cache&quot;&gt;Set-Associative Cache&lt;&#x2F;h3&gt;
&lt;p&gt;A 4-way set-associative cache with 256 sets can be laid out like this:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Set 0:   [Slot0][Slot1][Slot2][Slot3]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Set 1:   [Slot0][Slot1][Slot2][Slot3]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Set 2:   [Slot0][Slot1][Slot2][Slot3]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Set 255: [Slot0][Slot1][Slot2][Slot3]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The lookup shape is:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;set_index = hash(key) % set_count&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;set = sets[set_index]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;for slot in set:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    if slot.is_present and slot.key == key:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        return slot.value&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;return null&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Again, this is pseudocode. The important property is not the surface syntax but the access pattern: the loop touches one small contiguous region, usually one cache line or a small handful of cache lines.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;4-why-set-associative-caches-are-cpu-cache-friendly&quot;&gt;4. Why Set-Associative Caches Are CPU-Cache Friendly&lt;&#x2F;h2&gt;
&lt;p&gt;Four properties combine to make this layout fast:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Contiguous memory layout.&lt;&#x2F;strong&gt; Slots or metadata for a set live in a compact array. When the CPU loads a 64-byte cache line, it can bring in several pieces of relevant metadata at once.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Bounded probing.&lt;&#x2F;strong&gt; Probe length is a fixed constant: 4, 8, 16, or another small associativity. The pipeline does not have to chase an unpredictable chain.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Fewer pointer indirections.&lt;&#x2F;strong&gt; Sequential scans over compact arrays are friendlier to caches and prefetchers than scattered linked nodes.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Predictable branches.&lt;&#x2F;strong&gt; A short fixed loop with simple comparisons tends to be easier for the branch predictor than data-dependent pointer chasing.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h3 id=&quot;side-by-side&quot;&gt;Side by Side&lt;&#x2F;h3&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Aspect&lt;&#x2F;th&gt;&lt;th&gt;Pointer-heavy chained hash map&lt;&#x2F;th&gt;&lt;th&gt;Set-associative cache&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Memory layout&lt;&#x2F;td&gt;&lt;td&gt;Scattered, pointer-heavy&lt;&#x2F;td&gt;&lt;td&gt;Contiguous, array-based&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Lookup locality&lt;&#x2F;td&gt;&lt;td&gt;Often poor&lt;&#x2F;td&gt;&lt;td&gt;Excellent within one set&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Cache-line efficiency&lt;&#x2F;td&gt;&lt;td&gt;Low for chain traversal&lt;&#x2F;td&gt;&lt;td&gt;Dense metadata and bounded scan&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Probe length&lt;&#x2F;td&gt;&lt;td&gt;Variable&lt;&#x2F;td&gt;&lt;td&gt;Fixed by associativity&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Eviction&lt;&#x2F;td&gt;&lt;td&gt;External&#x2F;global if used as a cache&lt;&#x2F;td&gt;&lt;td&gt;Local to one set&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;h3 id=&quot;cache-line-behavior&quot;&gt;Cache-Line Behavior&lt;&#x2F;h3&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Chained hash map:        Set-associative cache:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Load bucket pointer      Load compact set metadata&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Jump to node             Scan a bounded set of ways&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Maybe jump again         Compare candidate slots&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The set-associative layout trades global freedom for a probing pattern the CPU can handle well.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;5-if-set-associative-caches-are-so-good-why-use-ordinary-caches&quot;&gt;5. If Set-Associative Caches Are So Good, Why Use Ordinary Caches?&lt;&#x2F;h2&gt;
&lt;p&gt;Set-associative caches win on raw lookup predictability and hot-path locality.  General-purpose caches win on flexibility, richer policies, and often better hit ratios for messy application workloads.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;when-set-associative-caches-shine&quot;&gt;When Set-Associative Caches Shine&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;Hot-path, low-latency systems: CPU caches, databases, storage engines, and similar systems code.&lt;&#x2F;li&gt;
&lt;li&gt;Workloads where bounded worst-case lookup work matters.&lt;&#x2F;li&gt;
&lt;li&gt;Fixed memory budgets.&lt;&#x2F;li&gt;
&lt;li&gt;Compact metadata and predictable memory access.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;This is why CPU designers use set associativity for L1&#x2F;L2 caches: hardware cannot tolerate pointer chasing or unbounded probe lengths in the common path.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;their-weaknesses&quot;&gt;Their Weaknesses&lt;&#x2F;h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Local eviction can be suboptimal.&lt;&#x2F;strong&gt; A hot set may evict useful entries while cold sets remain underused. A global policy may achieve a better hit ratio at the same capacity.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Fixed associativity can thrash.&lt;&#x2F;strong&gt; If more than &lt;code&gt;N&lt;&#x2F;code&gt; hot keys map to an &lt;code&gt;N&lt;&#x2F;code&gt;-way set, the set can churn even when the total cache has spare-looking capacity elsewhere.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Replacement policies are usually simple.&lt;&#x2F;strong&gt; LRU-within-set, CLOCK-like policies, and random replacement are common. Feature-rich application caches may offer TTLs, weights, admission policies, async refresh, and other workload-specific behavior.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Capacity is usually fixed.&lt;&#x2F;strong&gt; General-purpose caches often grow, shrink, or resize under memory pressure. Set-associative caches are normally sized up front.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h3 id=&quot;why-ordinary-caches-still-dominate-application-code&quot;&gt;Why Ordinary Caches Still Dominate Application Code&lt;&#x2F;h3&gt;
&lt;p&gt;Application caches often care more about policy than nanoseconds. They need dynamic sizing, TTLs, weighted entries, observability, workload-specific admission rules, and global eviction behavior. A set-associative cache is a sharp tool for a hot path, not a universal replacement for a full cache library.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;analogy&quot;&gt;Analogy&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;A CPU L1 cache is fast, compact, and set-associative, but not globally fair.&lt;&#x2F;li&gt;
&lt;li&gt;An OS page cache uses broader replacement machinery because it is optimizing a larger and messier working set.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Application code usually wants the page-cache model. Systems code on a hot path often wants the L1-cache model.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;6-case-study-tigerbeetle&quot;&gt;6. Case Study: TigerBeetle&lt;&#x2F;h2&gt;
&lt;p&gt;TigerBeetle is a distributed financial accounting database where correctness, latency, and predictability matter. Its &lt;code&gt;CacheMap&lt;&#x2F;code&gt; is a useful real-world example because it combines a fast set-associative tier with a bounded &lt;code&gt;std.HashMapUnmanaged&lt;&#x2F;code&gt; stash, and because the set-associative tier has received careful low-level optimization.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;6-1-cachemap-a-two-tier-in-memory-cache&quot;&gt;6.1 &lt;code&gt;CacheMap&lt;&#x2F;code&gt;: A Two-Tier In-Memory Cache&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;code&gt;CacheMap&lt;&#x2F;code&gt; lives in TigerBeetle&#x27;s LSM lookup path. The &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tigerbeetle&#x2F;tigerbeetle&#x2F;blob&#x2F;main&#x2F;src&#x2F;lsm&#x2F;cache_map.zig&quot;&gt;&lt;code&gt;cache_map.zig&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; source describes it as a hybrid between &lt;code&gt;SetAssociativeCache&lt;&#x2F;code&gt; and a &lt;code&gt;HashMap&lt;&#x2F;code&gt; stash:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;SetAssociativeCache&lt;&#x2F;code&gt; fast tier.&lt;&#x2F;strong&gt; A compact structure optimized for predictable lookup and insertion. Bounded associativity gives constant-time probing and local replacement.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;HashMap&lt;&#x2F;code&gt; stash.&lt;&#x2F;strong&gt; A preallocated &lt;code&gt;std.HashMapUnmanaged&lt;&#x2F;code&gt; used as an auxiliary tier. It catches values evicted from the set-associative tier and helps guarantee that prefetched values remain in memory during their respective commit.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;This is reminiscent of the &quot;stash&quot; idea used with cuckoo hashing: a small auxiliary structure catches cases the primary structure cannot cheaply retain.  The setting is different here, but the shape is similar. See the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.eecs.harvard.edu&#x2F;~michaelm&#x2F;postscripts&#x2F;esa2008full.pdf&quot;&gt;cuckoo-stash paper&lt;&#x2F;a&gt; for the underlying idea.&lt;&#x2F;p&gt;
&lt;p&gt;Crucially, the stash is not a complete in-memory copy of the database. The lookup hierarchy in TigerBeetle&#x27;s &lt;code&gt;cache_map.zig&lt;&#x2F;code&gt; is:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;cache -&amp;gt; stash -&amp;gt; immutable table -&amp;gt; LSM&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;A miss in &lt;code&gt;CacheMap&lt;&#x2F;code&gt; is therefore not a correctness failure. &lt;code&gt;CacheMap.get()&lt;&#x2F;code&gt; checks the set-associative cache and then the stash; the broader LSM lookup path can continue below that. Correctness belongs to the whole hierarchy, not to the stash alone.&lt;&#x2F;p&gt;
&lt;p&gt;The stash&#x27;s job is narrower: it gives hard in-memory availability for specific short-lived cases, such as prefetched values during commit, and catches values evicted from the fast tier.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;6-2-bounded-by-capacity-invalidated-by-compaction&quot;&gt;6.2 Bounded by Capacity, Invalidated by Compaction&lt;&#x2F;h3&gt;
&lt;p&gt;A natural objection is: does the stash retain entries indefinitely? No. Two separate mechanisms matter:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Bounded by construction.&lt;&#x2F;strong&gt; &lt;code&gt;CacheMap.init()&lt;&#x2F;code&gt; calls &lt;code&gt;ensureTotalCapacity()&lt;&#x2F;code&gt; with &lt;code&gt;stash_value_count_max&lt;&#x2F;code&gt;, and later stash updates use assume-capacity operations. The code path is designed around fixed allocation rather than unbounded runtime growth.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Cleared by compaction.&lt;&#x2F;strong&gt; &lt;code&gt;CacheMap.compact()&lt;&#x2F;code&gt; clears the stash while retaining its allocated capacity. The source comment is explicit that stash invalidation is handled by &lt;code&gt;compact&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Driven by the LSM&#x27;s compaction cadence.&lt;&#x2F;strong&gt; TigerBeetle&#x27;s LSM compaction runs incrementally in &quot;beats&quot; after commits. In Groove, the object cache is compacted on the last beat of the compaction bar, aligning cache invalidation with a deterministic storage-engine event.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The correctness invariant is intentionally narrow: call &lt;code&gt;compact()&lt;&#x2F;code&gt; only when the stash entries no longer need to provide their in-memory guarantee. If the cache is undersized, the operational cost is more misses and IO, not unbounded stash growth.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;6-3-tigerbeetle-s-setassociativecache-layout&quot;&gt;6.3 TigerBeetle&#x27;s &lt;code&gt;SetAssociativeCache&lt;&#x2F;code&gt; Layout&lt;&#x2F;h3&gt;
&lt;p&gt;The current default layout in
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tigerbeetle&#x2F;tigerbeetle&#x2F;blob&#x2F;main&#x2F;src&#x2F;lsm&#x2F;set_associative_cache.zig&quot;&gt;&lt;code&gt;src&#x2F;lsm&#x2F;set_associative_cache.zig&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; is more specific than the generic examples above:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;16 ways&lt;&#x2F;strong&gt; per set by default. The implementation currently allows 2, 4, or 16 ways for an efficient CLOCK-hand representation.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;8-bit tags&lt;&#x2F;strong&gt; by default, stored compactly for fast tag filtering. The code also supports 16-bit tags.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;2-bit per-way counts&lt;&#x2F;strong&gt; by default, used by a CLOCK Nth-Chance replacement policy.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Packed metadata.&lt;&#x2F;strong&gt; Tags, counts, and clock hands are laid out with cache-line-sized packing constraints. Values are stored separately and may be larger than a cache line.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;A lookup proceeds as follows:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Hash the key into a 64-bit entropy value.&lt;&#x2F;li&gt;
&lt;li&gt;Derive a short tag from that entropy.&lt;&#x2F;li&gt;
&lt;li&gt;Map the entropy to a set index.&lt;&#x2F;li&gt;
&lt;li&gt;SIMD-compare the set&#x27;s tags against the probed tag.&lt;&#x2F;li&gt;
&lt;li&gt;Do full key comparisons only for ways whose tag matched and whose count is nonzero.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;The tag compare is only a filter. An 8-bit tag can collide, so the final &lt;code&gt;key_from_value()&lt;&#x2F;code&gt; comparison is mandatory.&lt;&#x2F;p&gt;
&lt;p&gt;Two clarifications are worth keeping in mind:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;When the earlier sections talked about a &quot;single cache line,&quot; that is most precise for compact set metadata. Full values may be too large to fit in one line.&lt;&#x2F;li&gt;
&lt;li&gt;TigerBeetle does not use plain LRU or random replacement here. It uses a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.cse.iitd.ac.in&#x2F;~sbansal&#x2F;os&#x2F;lec&#x2F;l30.html&quot;&gt;CLOCK Nth-Chance&lt;&#x2F;a&gt; policy driven by packed per-way counts and a per-set clock hand.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;6-4-performance-deep-dive-pr-3200&quot;&gt;6.4 Performance Deep Dive: PR #3200&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tigerbeetle&#x2F;tigerbeetle&#x2F;pull&#x2F;3200&quot;&gt;PR #3200&lt;&#x2F;a&gt; restructured the SIMD search function to improve instruction-level parallelism and out-of-order execution. The benchmark deltas reported in the PR discussion are roughly +3.5%, +4.2%, +5.9%, and +5.2%, with improvements on both x86 and ARM machines and higher measured IPC.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;change-1-simd-tag-compare-to-integer-mask-via-bitcast&quot;&gt;Change 1: SIMD Tag Compare to Integer Mask via &lt;code&gt;@bitCast&lt;&#x2F;code&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;The set&#x27;s tags are compared against the probed tag using a Zig vector comparison:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;zig&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;const&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt; x:&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function&quot;&gt; @Vector&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt;(layout.ways, Tag) &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt; tags.&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;*&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;const&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt; y:&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function&quot;&gt; @Vector&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt;(layout.ways, Tag) &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function&quot;&gt; @splat&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt;(tag);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;const&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt; result:&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function&quot;&gt; @Vector&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt;(layout.ways,&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-type&quot;&gt; bool&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; x&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; ==&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt; y;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;return&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function&quot;&gt; @bitCast&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt;(result);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The resulting boolean vector is bit-cast into an integer mask whose set bits represent candidate ways. For the default 16-way layout, that mask is a &lt;code&gt;u16&lt;&#x2F;code&gt;. This is clean Zig and matches the current implementation.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;change-2-bit-iterator-to-fixed-trip-count-masked-scan&quot;&gt;Change 2: Bit Iterator to Fixed-Trip-Count Masked Scan&lt;&#x2F;h4&gt;
&lt;p&gt;The old shape consumed the mask by repeatedly finding the next set bit with &lt;code&gt;ctz&lt;&#x2F;code&gt;, clearing that bit, and looping. The new shape scans all ways and tests each bit:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;zig&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;const&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt; ways:&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-type&quot;&gt; u16&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; search_tags&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt;(set.tags, set.tag);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;if&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; (ways&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; ==&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;return null&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;for&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;0&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-variable&quot;&gt;..layout.ways) &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;|&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;way&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;|&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    if&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; ((ways&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; &amp;gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function&quot;&gt; @as&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-type&quot;&gt;u4&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function&quot;&gt; @intCast&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;(way)) &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;==&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; and&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt;        self.counts.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;get&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt;(set.offset&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; +&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; way) &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        if&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;key_from_value&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt;set.values[way]) &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;==&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; key) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;            return&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function&quot;&gt; @intCast&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt;(way);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;return null&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The explicit &lt;code&gt;@as(u4, @intCast(way))&lt;&#x2F;code&gt; is not decorative. In Zig, the right-hand side of a shift has a specific integer type. Since TigerBeetle&#x27;s current layout caps the default search at 16 ways, &lt;code&gt;u4&lt;&#x2F;code&gt; is enough to represent shift counts 0 through 15.&lt;&#x2F;p&gt;
&lt;p&gt;The code comment says the fixed scan is intended to help &lt;strong&gt;out-of-order execution&lt;&#x2F;strong&gt;. The reason is dependency shape. The bit-iterator loop had a &lt;strong&gt;loop-carried dependency&lt;&#x2F;strong&gt;: each iteration needed the mutated mask before it could know the next candidate way. The fixed scan leaves each bit test independent, which gives the compiler and CPU more freedom to overlap count loads, value loads, and comparisons.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;change-3-modulo-to-fastrange-and-simpler-tag-derivation&quot;&gt;Change 3: Modulo to &lt;code&gt;fastrange&lt;&#x2F;code&gt;, and Simpler Tag Derivation&lt;&#x2F;h4&gt;
&lt;p&gt;The index and tag derivation also changed:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;zig&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;const&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; entropy&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; hash&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt;(key);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;const&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt; tag: Tag&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function&quot;&gt; @truncate&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt;(entropy);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;const&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; index&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; fastrange&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt;(entropy, self.sets);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;const&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; offset&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; index&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; *&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt; layout.ways;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The important performance point is removing integer division from the hot path.  TigerBeetle&#x27;s &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tigerbeetle&#x2F;tigerbeetle&#x2F;blob&#x2F;main&#x2F;src&#x2F;stdx&#x2F;stdx.zig&quot;&gt;&lt;code&gt;stdx.fastrange()&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; implements &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;lemire.me&#x2F;blog&#x2F;2016&#x2F;06&#x2F;27&#x2F;a-fast-alternative-to-the-modulo-reduction&#x2F;&quot;&gt;Daniel Lemire&#x27;s multiply-high range reduction&lt;&#x2F;a&gt;: multiply a &lt;code&gt;u64&lt;&#x2F;code&gt; by the range as &lt;code&gt;u128&lt;&#x2F;code&gt;, then take the high 64 bits. That is not the same function as &lt;code&gt;%&lt;&#x2F;code&gt;, but it is appropriate for well-distributed hash-derived input when its bias properties are acceptable.&lt;&#x2F;p&gt;
&lt;p&gt;Do not blindly replace &lt;code&gt;%&lt;&#x2F;code&gt; with &lt;code&gt;fastrange&lt;&#x2F;code&gt; for structured or biased integers.  The TigerBeetle source includes a test named &quot;fastrange not modulo&quot; for exactly that reason.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;why-these-changes-help-ilp-and-ooo&quot;&gt;Why These Changes Help ILP and OoO&lt;&#x2F;h4&gt;
&lt;p&gt;&lt;strong&gt;A) Fixed trip count exposes independent work.&lt;&#x2F;strong&gt; The fixed loop tests &lt;code&gt;(ways &amp;gt;&amp;gt; way) &amp;amp; 1&lt;&#x2F;code&gt; independently for each way. With no mask mutation between iterations, the compiler can unroll more easily, and the out-of-order engine can overlap more address generation, count loads, and candidate value checks.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;B) Removing &lt;code&gt;ctz&lt;&#x2F;code&gt; plus bit-clearing shortens the serial chain.&lt;&#x2F;strong&gt; &lt;code&gt;ctz&lt;&#x2F;code&gt; is fast, but the old loop used it as part of a stateful iterator. Each step depended on the previous mask update. The fixed scan turns that into many small, regular tests.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;C) Fixed control flow is friendlier to the front end.&lt;&#x2F;strong&gt; A fixed &lt;code&gt;0..ways&lt;&#x2F;code&gt; loop has a simple shape. Even when it does more tiny tests, the regularity can be better for branch prediction, scheduling, and unrolling.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;D) &lt;code&gt;fastrange&lt;&#x2F;code&gt; removes a divider bubble.&lt;&#x2F;strong&gt; Integer division and modulo are high-latency operations on many CPUs. Replacing &lt;code&gt;% self.sets&lt;&#x2F;code&gt; with multiply-high range reduction shortens the hash-to-index-to-load chain.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;E) More candidate checks can be in flight.&lt;&#x2F;strong&gt; The new code may perform more scalar bit tests than the old bit iterator, but modern superscalar CPUs often prefer more independent work over fewer serialized operations. That is the core trade-off the PR exploits.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;why-more-work-can-be-faster&quot;&gt;Why &quot;More Work&quot; Can Be Faster&lt;&#x2F;h4&gt;
&lt;p&gt;The fixed scan can test all 16 ways even if only one tag matched. On paper, that looks wasteful. On a modern CPU, however, sixteen independent tiny tests can be cheaper than a smaller number of dependent operations. The PR&#x27;s benchmark results support that trade-off: throughput improved while IPC also improved.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;6-5-before-vs-after-a-schematic-pipeline-timeline&quot;&gt;6.5 Before vs. After: A Schematic Pipeline Timeline&lt;&#x2F;h3&gt;
&lt;p&gt;The timings below are illustrative. The exact cycle counts depend on the microarchitecture. The important shape is that a long serial operation disappears, and the candidate-scan loop loses a loop-carried dependency.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;before&quot;&gt;Before&lt;&#x2F;h4&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Time -&amp;gt;   0    4    8   12   16   20   24   28   32   36   40&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;------------------------------------------------------------------------&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Index     |------ integer DIV: entropy % sets ------| idx&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Tag                         shift&#x2F;truncate ----------&amp;gt; tag&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Set addr                              AGU ------------&amp;gt; base&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Tags load                                  L1&#x2F;L2 -----&amp;gt; tags&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;SIMD eq                                             ---&amp;gt; mask&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Bit-iterator loop:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Iter0: ctz(mask) -&amp;gt; way0; clearbit(mask) -&amp;gt; mask&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;       load count[way0]; maybe load value[way0]; compare&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Iter1 waits for mask&amp;#39;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;       ctz(mask&amp;#39;) -&amp;gt; way1; clearbit(mask&amp;#39;) -&amp;gt; mask&amp;#39;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;       load count[way1]; maybe load value[way1]; compare&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Iter2 waits for mask&amp;#39;&amp;#39; ...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The divider is on the critical path before the set load. The bit iterator then limits overlap because each iteration depends on the previous mask update.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;after&quot;&gt;After&lt;&#x2F;h4&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Time -&amp;gt;   0    4    8   12   16   20   24   28   32   36   40&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;------------------------------------------------------------------------&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Index     fastrange multiply&#x2F;shift -&amp;gt; idx&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Tag       truncate(entropy) -------&amp;gt; tag&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Set addr             AGU ----------&amp;gt; base&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Tags load                  L1&#x2F;L2 --&amp;gt; tags&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;SIMD eq                           -&amp;gt; mask via @bitCast&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Fixed loop, independent tests:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Way0:  (mask &amp;gt;&amp;gt; 0) &amp;amp; 1   load count[0]   maybe load value[0]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Way1:  (mask &amp;gt;&amp;gt; 1) &amp;amp; 1   load count[1]   maybe load value[1]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Way2:  (mask &amp;gt;&amp;gt; 2) &amp;amp; 1   load count[2]   maybe load value[2]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Way15: (mask &amp;gt;&amp;gt; 15) &amp;amp; 1  load count[15]  maybe load value[15]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The index is available sooner, and the candidate checks have less serial state. That gives the out-of-order engine more independent work to overlap.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;critical-path-summary&quot;&gt;Critical-Path Summary&lt;&#x2F;h4&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Stage&lt;&#x2F;th&gt;&lt;th&gt;Before&lt;&#x2F;th&gt;&lt;th&gt;After&lt;&#x2F;th&gt;&lt;th&gt;Why it matters&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Index computation&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;%&lt;&#x2F;code&gt; integer division&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;fastrange&lt;&#x2F;code&gt; multiply-high reduction&lt;&#x2F;td&gt;&lt;td&gt;Removes a long-latency divider from the path&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Candidate iteration&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;ctz&lt;&#x2F;code&gt; plus mask mutation&lt;&#x2F;td&gt;&lt;td&gt;Fixed loop with independent bit test&lt;&#x2F;td&gt;&lt;td&gt;Removes loop-carried dependency&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;SIMD to scalar handoff&lt;&#x2F;td&gt;&lt;td&gt;Pointer-cast-style handoff&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;@bitCast(result)&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;Clean conversion from vector result to mask&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Memory-level parallelism&lt;&#x2F;td&gt;&lt;td&gt;Fewer candidates in flight&lt;&#x2F;td&gt;&lt;td&gt;More candidates can overlap&lt;&#x2F;td&gt;&lt;td&gt;Better latency hiding on cache misses&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;7-takeaways&quot;&gt;7. Takeaways&lt;&#x2F;h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Fixed-count masked scans can beat variable-length bit iteration.&lt;&#x2F;strong&gt; If the old loop carries state from one iteration to the next, a fixed scan may expose more instruction-level parallelism even when it performs more tests.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Replacing &lt;code&gt;%&lt;&#x2F;code&gt; on a hot path can matter.&lt;&#x2F;strong&gt; Multiply-based range reduction, such as &lt;code&gt;fastrange&lt;&#x2F;code&gt;, can remove a high-latency divider when the input distribution makes it valid.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;SIMD filters work well with scalar verification.&lt;&#x2F;strong&gt; Compare compact tags in parallel, turn the result into a mask, then run full key comparisons only for candidate ways.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;A fast bounded tier can compose with a correctness-owning storage hierarchy.&lt;&#x2F;strong&gt; TigerBeetle&#x27;s set-associative tier is a hot cache. The stash catches evictions and prefetched values. The LSM below remains the source of truth.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Invalidation is easier when tied to a deterministic system event.&lt;&#x2F;strong&gt; Clearing the stash during compaction keeps the memory story simple and bounded.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Set-associative caches are not a universal hammer. They trade global replacement freedom for bounded, predictable access. In a hot path like TigerBeetle&#x27;s &lt;code&gt;CacheMap&lt;&#x2F;code&gt;, that trade-off is exactly the point.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Building a custom Bitset - A study in access-pattern-driven data structure choice</title>
          <pubDate>Mon, 25 May 2026 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://debasishg.github.io/blog/why-bitset/</link>
          <guid>https://debasishg.github.io/blog/why-bitset/</guid>
          <description xml:base="https://debasishg.github.io/blog/why-bitset/">&lt;h1 id=&quot;building-a-custom-bitset-a-study-in-access-pattern-driven-data-structure-choice&quot;&gt;Building a custom Bitset - A study in access-pattern-driven data structure choice&lt;&#x2F;h1&gt;
&lt;p&gt;I was porting &lt;code&gt;porcupine-rust&lt;&#x2F;code&gt; to Zig - the core of the implementation is a Bitset data structure, which is used in the implementation of a cache (see below for the details of why we need the cache).&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;debasishg&#x2F;porcupine-zig&quot;&gt;&lt;code&gt;porcupine-zig&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; carries two bitset implementations and uses them in two different places:&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Site&lt;&#x2F;th&gt;&lt;th&gt;Type&lt;&#x2F;th&gt;&lt;th&gt;Mode&lt;&#x2F;th&gt;&lt;th&gt;Purpose&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;src&#x2F;bitset.zig&lt;&#x2F;code&gt; (custom)&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;Bitset&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;Release + Debug&lt;&#x2F;td&gt;&lt;td&gt;Inner DFS in &lt;code&gt;checker.zig&lt;&#x2F;code&gt; - cache-probed, cloned per miss, hashed every step&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;assertPartitionIndependent&lt;&#x2F;code&gt; (std)&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;std.DynamicBitSet&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;Debug only&lt;&#x2F;td&gt;&lt;td&gt;One-shot validation that a partitioning is disjoint, complete, and in-bounds&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;That is not duplication or inconsistency. It is the most basic discipline in performance engineering: &lt;strong&gt;pick the data structure that fits the access pattern of the call site, not the one that fits &quot;bitset&quot; as a category.&lt;&#x2F;strong&gt; The two sites have effectively zero overlap in what they ask of a bitset, and collapsing them onto a single type would either pessimise the hot path or over-fit the cold one.&lt;&#x2F;p&gt;
&lt;p&gt;This document walks through both call sites, derives the requirements from the algorithm rather than from intuition, and shows why the resulting choices are strongly justified - not stylistic. A wrapper around &lt;code&gt;std.DynamicBitSetUnmanaged&lt;&#x2F;code&gt; could in principle furnish part of the hot-path surface (its allocator-on-call-site shape, &lt;code&gt;clone&lt;&#x2F;code&gt;, and &lt;code&gt;eql&lt;&#x2F;code&gt; are already there), but it would still lack SBO and the virtual-mutation hash&#x2F;equality protocol; closing that gap is most of the work, which is why the project carries a project-local type. For a complete explanation we need to start with a basic understanding of the linearizability-checker algorithm.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;1-the-hot-path-linearizability-dfs-in-checker-zig&quot;&gt;1. The hot path: linearizability DFS in &lt;code&gt;checker.zig&lt;&#x2F;code&gt;&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;1-1-what-the-algorithm-actually-does&quot;&gt;1.1 What the algorithm actually does&lt;&#x2F;h3&gt;
&lt;p&gt;The Wing &amp;amp; Gong &#x2F; Lowe linearizability check explored in &lt;code&gt;checkSingle&lt;&#x2F;code&gt; is a depth-first search over the lattice of partial linearizations of an operation history. At each frame the algorithm:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Picks an unlinearized call &lt;code&gt;op_id&lt;&#x2F;code&gt; from a doubly-linked candidate list.&lt;&#x2F;li&gt;
&lt;li&gt;Steps the user-supplied model: &lt;code&gt;step(state, input, output) -&amp;gt; ?State&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;If the step succeeds, asks the cache: &lt;em&gt;have we already explored the &lt;code&gt;(linearized ∪ {op_id}, new_state)&lt;&#x2F;code&gt; pair from any other ordering?&lt;&#x2F;em&gt;&lt;&#x2F;li&gt;
&lt;li&gt;On cache miss: commit. Push the old state onto a backtrack stack, set the bit, store the new &lt;code&gt;(bitset, state)&lt;&#x2F;code&gt; in the cache, descend.&lt;&#x2F;li&gt;
&lt;li&gt;On cache hit: prune. Backtrack and try the next candidate.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;The cache is the entire point of the algorithm. Without it the search is super-exponential in history length; with it the redundant subtrees collapse and the search is tractable for histories of a few hundred operations. The cache key is the pair &lt;code&gt;(set of linearized op ids, model state)&lt;&#x2F;code&gt;, with the bitset standing in for the set.&lt;&#x2F;p&gt;
&lt;p&gt;This puts the bitset squarely in the DFS inner loop - touched multiple times per frame:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Hashed&lt;&#x2F;strong&gt; every probe (&lt;code&gt;linearized.hashWithBit(op_id)&lt;&#x2F;code&gt;).&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Compared for equality&lt;&#x2F;strong&gt; every probe whose hash collides (&lt;code&gt;bs.eqlWithBit(...)&lt;&#x2F;code&gt;, inside &lt;code&gt;cacheContainsWithBit&lt;&#x2F;code&gt;).&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Cloned&lt;&#x2F;strong&gt; every cache miss, before the bit is actually set on the clone (&lt;code&gt;linearized.clone(arena_alloc)&lt;&#x2F;code&gt;).&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Mutated&lt;&#x2F;strong&gt; on every step forward and every backtrack (&lt;code&gt;linearized.set(op_id)&lt;&#x2F;code&gt;).&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;On workloads shaped like the Rust&#x2F;Go etcd and KV fixtures, cache hits are expected to dominate mid-search (the upstream Go and Rust ports report high hit rates on these histories; a measured Zig hit-rate table belongs here once the corresponding fixtures land in &lt;code&gt;porcupine-zig&lt;&#x2F;code&gt;). That distribution matters: any cost that lands on a &lt;em&gt;probe&lt;&#x2F;em&gt; is paid many times more often than any cost that lands on a &lt;em&gt;commit&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;1-2-requirements-derived-from-the-access-pattern&quot;&gt;1.2 Requirements derived from the access pattern&lt;&#x2F;h3&gt;
&lt;p&gt;From the algorithm we get a precise contract for the bitset:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Cheap clone in the common size range.&lt;&#x2F;strong&gt; Histories the project actually targets are 50-256 ops; cloning must not allocate in this range, because misses still happen at every level of the search tree.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;hash&lt;&#x2F;code&gt; that admits an O(1) update under single-bit mutation.&lt;&#x2F;strong&gt; Every probe asks &quot;what would the hash be if I set bit &lt;code&gt;p&lt;&#x2F;code&gt;?&quot;. If answering that requires materializing the mutated bitset, the probe pays the cost of the commit even when it&#x27;s a hit.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;eq&lt;&#x2F;code&gt; that can be evaluated against a virtually-mutated &lt;code&gt;self&lt;&#x2F;code&gt;.&lt;&#x2F;strong&gt; Symmetric to (2): hash collisions need to be resolved without realising the mutation either.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Allocator passed at the call site, not stored.&lt;&#x2F;strong&gt; The DFS uses a per-worker arena (constructed inside &lt;code&gt;checkSingle&lt;&#x2F;code&gt;) and tears the whole partition down with one &lt;code&gt;arena.deinit()&lt;&#x2F;code&gt;. A bitset that hides an allocator pointer inside its struct fights that lifetime model.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Stable inline layout, predictable indexing.&lt;&#x2F;strong&gt; Hot inner loops want the chunk pointer in a register and bounds checks elided in release.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;The standard library&#x27;s &lt;code&gt;std.DynamicBitSet&lt;&#x2F;code&gt; (and &lt;code&gt;DynamicBitSetUnmanaged&lt;&#x2F;code&gt;) satisfy &lt;em&gt;none&lt;&#x2F;em&gt; of (1)-(3) and only partially (4)-(5). &lt;code&gt;DynamicBitSetUnmanaged&lt;&#x2F;code&gt; already takes the allocator at the call site and exposes &lt;code&gt;clone&lt;&#x2F;code&gt;&#x2F;&lt;code&gt;eql&lt;&#x2F;code&gt;, so the lifetime and basic-comparison story can be borrowed; but SBO and the virtual-mutation hash&#x2F;equality protocol have no counterpart in the standard library, and adding them is most of the work of writing a project-local type.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;1-3-how-the-custom-bitset-answers-each-requirement&quot;&gt;1.3 How the custom &lt;code&gt;Bitset&lt;&#x2F;code&gt; answers each requirement&lt;&#x2F;h3&gt;
&lt;h4 id=&quot;1-3-1-small-buffer-optimization-sbo&quot;&gt;1.3.1 Small-buffer optimization (SBO)&lt;&#x2F;h4&gt;
&lt;p&gt;&lt;code&gt;Bitset&lt;&#x2F;code&gt; stores an &lt;code&gt;inline_buf: [4]u64&lt;&#x2F;code&gt; field directly in the struct - 256 bits, aligned, contiguous, no indirection. The &lt;code&gt;heap&lt;&#x2F;code&gt; slice is consulted only when the bit count exceeds 256, which on the project&#x27;s workloads happens for exactly one fixture (and even there only for the un-partitioned variant).&lt;&#x2F;p&gt;
&lt;p&gt;What this buys, concretely:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;clone&lt;&#x2F;code&gt; is a struct copy plus an &lt;em&gt;optional&lt;&#x2F;em&gt; &lt;code&gt;allocator.dupe&lt;&#x2F;code&gt; that almost never runs. In the inline case it is one &lt;code&gt;[4]u64&lt;&#x2F;code&gt; move + a couple of scalar fields. No allocator vtable dispatch, no free-list traversal, no fragmentation.&lt;&#x2F;li&gt;
&lt;li&gt;The active storage tends to live in 1-4 consecutive cache lines, often prefetched as part of the surrounding &lt;code&gt;CacheEntry&lt;&#x2F;code&gt; row, rather than being a heap pointer that may sit on a cold line.&lt;&#x2F;li&gt;
&lt;li&gt;The arena allocator never sees most bitsets at all. On the partitions that fit inline the arena reset at end-of-partition is reclaiming state-clones and hash-table buckets, not bitset bodies.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The 256-bit ceiling is not arbitrary: 256 was chosen because (a) it covers the histories &lt;code&gt;porcupine-zig&lt;&#x2F;code&gt; is benchmarked against (etcd ~170 ops → 3 chunks; KV per-partition ≤ 50 ops → 1 chunk, per the workload notes at the top of &lt;code&gt;bitset.zig&lt;&#x2F;code&gt;), and (b) &lt;code&gt;n &#x2F; 64&lt;&#x2F;code&gt; is a single shift in code that indexes chunks, with &lt;code&gt;inline_cap = 4&lt;&#x2F;code&gt; keeping the struct under one cache line on common architectures (&lt;code&gt;Bitset&lt;&#x2F;code&gt; is &lt;code&gt;4*8 + 16 + 8 = 56&lt;&#x2F;code&gt; bytes, plus padding).&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;std.DynamicBitSet&lt;&#x2F;code&gt; has no small-buffer optimization; for any nonzero runtime size its mask storage is heap-backed. The type is designed for arbitrary runtime sizes where the average case is large enough to not care about the allocation. For the DFS this is the wrong default: the average case is &lt;em&gt;small enough that the allocation would dominate the operation cost&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;(One Zig-specific ownership caveat worth flagging while we&#x27;re on the SBO discussion: &lt;code&gt;Bitset&lt;&#x2F;code&gt; contains a slice field for the heap-spilled case, so a plain struct assignment of a heap-spilled bitset is a shallow copy that aliases the heap chunks. The source comments require callers that need a deep copy to go through &lt;code&gt;clone(allocator)&lt;&#x2F;code&gt;. The DFS already does, but the discipline is part of what makes the explicit allocator-per-call shape work.)&lt;&#x2F;p&gt;
&lt;h4 id=&quot;1-3-2-algebraically-updateable-hash&quot;&gt;1.3.2 Algebraically updateable hash&lt;&#x2F;h4&gt;
&lt;p&gt;The &lt;code&gt;hash&lt;&#x2F;code&gt; formula is&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;zig&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;h&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; popcnt&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;(bits)            &lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt;&#x2F;&#x2F; initial value&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;for&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt; each chunk c: h&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; ^=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; c&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt;    &#x2F;&#x2F; fold chunks in&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This looks pedestrian, but the &lt;em&gt;chunk fold&lt;&#x2F;em&gt; has a property no off-the-shelf hash (FNV, xxHash, FxHash, SipHash) gives you: it is &lt;em&gt;XOR-decomposable in the chunk basis&lt;&#x2F;em&gt;. Setting a single bit &lt;code&gt;p&lt;&#x2F;code&gt; mutates exactly one chunk word, from &lt;code&gt;old_word&lt;&#x2F;code&gt; to &lt;code&gt;new_word = old_word | (1 &amp;lt;&amp;lt; minor)&lt;&#x2F;code&gt;, and the chunk fold&#x27;s contribution shifts by exactly &lt;code&gt;(old_word ^ new_word)&lt;&#x2F;code&gt;. The popcnt seed shifts independently - by &lt;code&gt;popcnt XOR (popcnt + 1)&lt;&#x2F;code&gt;, which is &lt;code&gt;1&lt;&#x2F;code&gt; when popcnt is even and a longer carry chain when odd.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;hashWithBit&lt;&#x2F;code&gt; returns a closed-form &lt;em&gt;lookup key&lt;&#x2F;em&gt; that folds in the chunk delta and the even-popcnt shape of the popcnt delta:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;zig&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;hashWithBit&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;(p) &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt; self.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;hash&lt;&#x2F;span&gt;&lt;span&gt;() &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;^&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; old_word&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; ^&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; new_word&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; ^&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Two technical notes on what this actually is and is not:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;It is a self-consistent cache key, not the post-set &lt;code&gt;hash()&lt;&#x2F;code&gt;.&lt;&#x2F;strong&gt; When popcnt is odd, the true popcnt delta is wider than &lt;code&gt;1&lt;&#x2F;code&gt; (e.g., &lt;code&gt;3 → 4&lt;&#x2F;code&gt; differs by &lt;code&gt;0b111&lt;&#x2F;code&gt;), so &lt;code&gt;hashWithBit(p)&lt;&#x2F;code&gt; does not in general equal &lt;code&gt;hash()&lt;&#x2F;code&gt; of the bitset &lt;em&gt;after&lt;&#x2F;em&gt; &lt;code&gt;set(p)&lt;&#x2F;code&gt;. What it does guarantee is &lt;em&gt;self-consistency&lt;&#x2F;em&gt;: any two probes that would land on the same &lt;code&gt;(linearized + bit, state)&lt;&#x2F;code&gt; cache entry have equal predecessor popcnts and equal post-set chunks, so they hash to the same value under this formula. Since insertion and lookup both go through &lt;code&gt;hashWithBit&lt;&#x2F;code&gt;, the cache&#x27;s hit rate depends only on self-consistency - and self-consistency holds for every &lt;code&gt;popcnt&lt;&#x2F;code&gt; parity. (The function name and the unit test in &lt;code&gt;bitset.zig&lt;&#x2F;code&gt; both suggest the stronger property; only the weaker one is load-bearing for the algorithm.)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;It is &lt;code&gt;O(chunks)&lt;&#x2F;code&gt;, not &lt;code&gt;O(1)&lt;&#x2F;code&gt;, and currently makes two passes over the active chunks.&lt;&#x2F;strong&gt; &lt;code&gt;self.hash()&lt;&#x2F;code&gt; first calls &lt;code&gt;popcnt()&lt;&#x2F;code&gt;, which walks &lt;code&gt;self.data()&lt;&#x2F;code&gt; to count set bits, and then loops over &lt;code&gt;self.data()&lt;&#x2F;code&gt; again to fold the chunks. &lt;code&gt;hashWithBit&lt;&#x2F;code&gt; adds the three-XOR adjustment on top. The structural saving in the hot path therefore comes from &lt;code&gt;hashWithBit&lt;&#x2F;code&gt; not having to &lt;em&gt;allocate, clone, or mutate&lt;&#x2F;em&gt;, not from skipping the chunk scan. Fusing the popcount and the XOR fold into a single loop is a trivial micro-optimisation; a more substantive version that maintained &lt;code&gt;popcnt&lt;&#x2F;code&gt; and the running chunk-XOR incrementally on &lt;code&gt;set&lt;&#x2F;code&gt;&#x2F;&lt;code&gt;clear&lt;&#x2F;code&gt; - and used the proper &lt;code&gt;popcnt XOR (popcnt + 1)&lt;&#x2F;code&gt; delta - would deliver true &lt;code&gt;O(1)&lt;&#x2F;code&gt; hashing and post-set equivalence as a free side-effect. The current code chose simpler state.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The architectural point still stands: chunk-level XOR-decomposition is what makes &lt;em&gt;any&lt;&#x2F;em&gt; incremental scheme possible. Swapping in FxHash, xxHash, or SipHash would force every probe to materialize the post-set bitset and rehash from scratch - which is exactly what the Go original does. The hash, the SBO, and the deferred-clone protocol form one design; you can&#x27;t extract any single piece and keep the others.&lt;&#x2F;p&gt;
&lt;p&gt;The hash table the cache lives in (an &lt;code&gt;std.HashMapUnmanaged&lt;&#x2F;code&gt; in the checker&#x27;s &lt;code&gt;CacheEntry&lt;&#x2F;code&gt; map) is keyed by a precomputed &lt;code&gt;u64&lt;&#x2F;code&gt; produced by &lt;code&gt;hashWithBit&lt;&#x2F;code&gt;, and the table uses the identity context &lt;code&gt;U64IdentityCtx&lt;&#x2F;code&gt; to consume that &lt;code&gt;u64&lt;&#x2F;code&gt; directly as the bucket hash. The library&#x27;s default context would re-hash the &lt;code&gt;u64&lt;&#x2F;code&gt;, layering an unnecessary mixing pass over a value that already came out of a hash-shaped formula. Note the precise claim: the stored key is the &lt;code&gt;hashWithBit&lt;&#x2F;code&gt; value, not necessarily &lt;code&gt;Bitset.hash()&lt;&#x2F;code&gt; of the post-set bitset (on odd predecessor popcounts those differ, per the parity caveat above). The identity context is correct because the cache key is precomputed, not because &lt;code&gt;Bitset.hash()&lt;&#x2F;code&gt; is universally the table key.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;1-3-3-deferred-clone-cache-probing&quot;&gt;1.3.3 Deferred-clone cache probing&lt;&#x2F;h4&gt;
&lt;p&gt;Equipped with &lt;code&gt;hashWithBit&lt;&#x2F;code&gt;, the matching &lt;code&gt;eqlWithBit&lt;&#x2F;code&gt; closes the loop. Equality between &quot;self with bit &lt;code&gt;p&lt;&#x2F;code&gt; set&quot; and an existing cache entry is checked by walking the chunk arrays in lockstep and OR-ing the relevant word on the fly:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;zig&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;const&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; set_mask&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function&quot;&gt; @as&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-type&quot;&gt;u64&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;&amp;lt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt; ix.minor;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;for&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt; (a, b,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;..) &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;|&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt;x, y, i&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;|&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    const&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; adj&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; if&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; (i&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; ==&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt; ix.major) x&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; |&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; set_mask&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; else&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt; x;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    if&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; (adj&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; !=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; y) &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;return false&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The hot-path call site is &lt;code&gt;cacheContainsWithBit&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;zig&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;const&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; h&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt; linearized.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;hashWithBit&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt;(op_id);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;if&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;!&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;cacheContainsWithBit&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt;(M,&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt;cache, model, h,&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt;linearized, op_id,&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;ns)) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-comment&quot;&gt;    &#x2F;&#x2F; miss: now we pay for clone + set + cache insert&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    var&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; new_linearized&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; try&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt; linearized.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;clone&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt;(arena_alloc);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt;    new_linearized.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;set&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt;(op_id);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;    ...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The structural consequence: on every probe that hits the cache, the algorithm never allocates a bitset, never mutates a bitset, and never copies its chunks into a temporary. It hashes the predecessor (two chunk passes today, as discussed above), adjusts with three XORs, walks one hash bucket&#x27;s chain (&lt;code&gt;eqlWithBit&lt;&#x2F;code&gt; synthesizing the post-set word inline), and prunes. What is &lt;em&gt;deferred&lt;&#x2F;em&gt; is the clone and the bit-set: for inline bitsets that&#x27;s a &lt;code&gt;[4]u64&lt;&#x2F;code&gt; struct copy plus a single OR; for heap-spilled bitsets it&#x27;s an &lt;code&gt;allocator.dupe&lt;&#x2F;code&gt; plus a memcpy plus an OR. The clone happens only on the cache-miss branch, where the bitset is about to be stored anyway and the allocation amortises against the work of the new DFS subtree. The size of the win on a probe-heavy workload is therefore proportional to the probe-to-commit ratio, which is the property the algorithm-level numbers from the Go and Rust ports describe.&lt;&#x2F;p&gt;
&lt;p&gt;This optimisation is present in the Rust port (where it is named &lt;code&gt;hash_with_bit&lt;&#x2F;code&gt;) and absent in the Go original, which clones-then-hashes on every probe. The performance gap on long histories is meaningful for that exact reason.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;std.DynamicBitSet&lt;&#x2F;code&gt; exposes no &lt;code&gt;hash&lt;&#x2F;code&gt; method at all, let alone an algebraically-updateable one. Bolting it on would require deciding the hash formula, which immediately re-introduces the design choice that drives the custom type.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;1-3-4-allocator-on-the-call-site&quot;&gt;1.3.4 Allocator on the call site&lt;&#x2F;h4&gt;
&lt;p&gt;The bitset stores no allocator handle. Every allocating method takes one explicitly:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;zig&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-storage z-type z-function&quot;&gt;pub fn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; init&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt;(allocator: std.mem.Allocator, n:&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-type&quot;&gt; usize&lt;&#x2F;span&gt;&lt;span&gt;)         &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;!&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;Bitset&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-storage z-type z-function&quot;&gt;pub fn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; deinit&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt;(self:&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; *&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt;Bitset, allocator: std.mem.Allocator)  &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-type&quot;&gt;void&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-storage z-type z-function&quot;&gt;pub fn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; clone&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt;(self:&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; *&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;const&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt; Bitset, allocator: std.mem.Allocator) &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;!&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;Bitset&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This matches the DFS&#x27;s lifetime model. &lt;code&gt;checkSingle&lt;&#x2F;code&gt; constructs an &lt;code&gt;std.heap.ArenaAllocator&lt;&#x2F;code&gt; per partition (per worker thread when partitions parallelise) and feeds &lt;em&gt;every&lt;&#x2F;em&gt; bitset, &lt;em&gt;every&lt;&#x2F;em&gt; state clone, &lt;em&gt;every&lt;&#x2F;em&gt; hash-map bucket through that arena. At the end of the partition the arena is reset in one operation; thousands of bitsets and tens of thousands of hash buckets are reclaimed together with no per-object teardown.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;std.DynamicBitSet&lt;&#x2F;code&gt; (the managed variant) embeds an &lt;code&gt;Allocator&lt;&#x2F;code&gt; pointer in the struct. Storing thousands of these in the cache would cost one extra pointer per entry for a value that is constant across all entries.  &lt;code&gt;DynamicBitSetUnmanaged&lt;&#x2F;code&gt; solves that, but still leaves you without SBO, without &lt;code&gt;hash&lt;&#x2F;code&gt;&#x2F;&lt;code&gt;hashWithBit&lt;&#x2F;code&gt;, and without &lt;code&gt;eqlWithBit&lt;&#x2F;code&gt;. The unmanaged form is closer to the right shape, but at that point you would be writing the custom type around it.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;1-3-5-small-inlineable-indexing-primitives&quot;&gt;1.3.5 Small, inlineable indexing primitives&lt;&#x2F;h4&gt;
&lt;p&gt;&lt;code&gt;isSet&lt;&#x2F;code&gt;, &lt;code&gt;set&lt;&#x2F;code&gt;, &lt;code&gt;clear&lt;&#x2F;code&gt;, and &lt;code&gt;index&lt;&#x2F;code&gt; are declared &lt;code&gt;inline fn&lt;&#x2F;code&gt;, and the chunk-pointer selection is a single &lt;code&gt;chunks &amp;gt; inline_cap&lt;&#x2F;code&gt; branch against a field that is constant for the lifetime of a given bitset. The &lt;code&gt;index&lt;&#x2F;code&gt; helper splits &lt;code&gt;pos&lt;&#x2F;code&gt; into &lt;code&gt;(major, minor)&lt;&#x2F;code&gt; with &lt;code&gt;pos &#x2F; 64&lt;&#x2F;code&gt; and &lt;code&gt;pos % 64&lt;&#x2F;code&gt;; since 64 is a power of two and &lt;code&gt;pos&lt;&#x2F;code&gt; is a &lt;code&gt;usize&lt;&#x2F;code&gt;, this is the canonical shape the compiler can lower to a shift and an &lt;code&gt;AND&lt;&#x2F;code&gt;. In &lt;code&gt;-Doptimize=ReleaseFast&lt;&#x2F;code&gt;, the &lt;code&gt;debug.assert&lt;&#x2F;code&gt; bounds checks fall away and the inline-vs-heap branch is predictable enough to be cheap (and in many call sites, hoistable). The point is not a specific assembly sequence - that would require a disassembly listing this post does not include - but that the primitives are small and shaped to be optimised, which matters because they run once per node visited in the DFS.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;1-4-summary-table-for-the-hot-path&quot;&gt;1.4 Summary table for the hot path&lt;&#x2F;h3&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Requirement&lt;&#x2F;th&gt;&lt;th&gt;Custom &lt;code&gt;Bitset&lt;&#x2F;code&gt;&lt;&#x2F;th&gt;&lt;th&gt;&lt;code&gt;std.DynamicBitSet&lt;&#x2F;code&gt;&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;No allocation for histories ≤ 256 ops&lt;&#x2F;td&gt;&lt;td&gt;Yes (SBO)&lt;&#x2F;td&gt;&lt;td&gt;No&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;hash()&lt;&#x2F;code&gt; defined&lt;&#x2F;td&gt;&lt;td&gt;Yes, XOR-decomposable&lt;&#x2F;td&gt;&lt;td&gt;No&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Probe a &quot;virtually-mutated&quot; key&lt;&#x2F;td&gt;&lt;td&gt;Yes (&lt;code&gt;hashWithBit&lt;&#x2F;code&gt;&#x2F;&lt;code&gt;eqlWithBit&lt;&#x2F;code&gt;)&lt;&#x2F;td&gt;&lt;td&gt;Not expressible without re-implementing the type&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Allocator passed per call (arena-friendly)&lt;&#x2F;td&gt;&lt;td&gt;Yes&lt;&#x2F;td&gt;&lt;td&gt;Managed: no. Unmanaged: yes (but still missing the above).&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Wire-compatible hash with Go&#x2F;Rust ports&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;hash()&lt;&#x2F;code&gt; formula matches; &lt;code&gt;hashWithBit&lt;&#x2F;code&gt; is a self-consistent cache key, not always equal to the post-set &lt;code&gt;hash()&lt;&#x2F;code&gt; (parity caveat)&lt;&#x2F;td&gt;&lt;td&gt;N&#x2F;A&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;2-the-cold-path-assertpartitionindependent&quot;&gt;2. The cold path: &lt;code&gt;assertPartitionIndependent&lt;&#x2F;code&gt;&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;2-1-what-it-is-for&quot;&gt;2.1 What it is for&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;code&gt;assertPartitionIndependent&lt;&#x2F;code&gt; validates a debug-mode invariant: when a model declares a &lt;code&gt;partition&lt;&#x2F;code&gt; hook, the returned list of partitions must be a &lt;em&gt;partition&lt;&#x2F;em&gt; of &lt;code&gt;[0, N)&lt;&#x2F;code&gt; in the mathematical sense. Specifically:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Disjoint&lt;&#x2F;strong&gt; - no index appears in two partitions.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Complete&lt;&#x2F;strong&gt; - every index in &lt;code&gt;[0, N)&lt;&#x2F;code&gt; appears at least once.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;In-bounds&lt;&#x2F;strong&gt; - no index is &lt;code&gt;≥ N&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;A bug in a user-supplied &lt;code&gt;partitionEvents&lt;&#x2F;code&gt; could otherwise cause the DFS to silently skip events (violating soundness) or read past the history (a panic, but not a useful error).&lt;&#x2F;p&gt;
&lt;h3 id=&quot;2-2-access-pattern&quot;&gt;2.2 Access pattern&lt;&#x2F;h3&gt;
&lt;p&gt;The function runs at most once per &lt;code&gt;checkOperations&lt;&#x2F;code&gt; &#x2F; &lt;code&gt;checkEvents&lt;&#x2F;code&gt; invocation that actually receives partitions from the user-supplied &lt;code&gt;partition&lt;&#x2F;code&gt; &#x2F; &lt;code&gt;partitionEvents&lt;&#x2F;code&gt; hook, and it compiles to a no-op outside Debug mode. When it does run, it performs:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;One allocation of a bit array of length &lt;code&gt;N&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;One pass over all partitions; for each index, one bounds check, one &lt;code&gt;isSet&lt;&#x2F;code&gt; test, one &lt;code&gt;set&lt;&#x2F;code&gt;, one increment.&lt;&#x2F;li&gt;
&lt;li&gt;One terminal equality check on the count.&lt;&#x2F;li&gt;
&lt;li&gt;One deinit.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;That is it. No clone, no hash, no equality, no nested probes, no per-step mutation across DFS frames, no allocator threading. The function exists to fail loudly on invalid partitionings, not to be fast.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;2-3-requirements-derived-from-the-access-pattern&quot;&gt;2.3 Requirements derived from the access pattern&lt;&#x2F;h3&gt;
&lt;p&gt;Reading the algorithm rather than the type:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Allocate &lt;code&gt;N&lt;&#x2F;code&gt; bits, set each, query each, count.&lt;&#x2F;strong&gt; Nothing else.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Lifetime is one stack frame.&lt;&#x2F;strong&gt; No need for arena routing or for the bitset to outlive the function.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Performance is irrelevant.&lt;&#x2F;strong&gt; This path is removed in release builds.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Code clarity is paramount.&lt;&#x2F;strong&gt; This is an assertion: future readers should be able to see at a glance that the three invariants are enforced.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h3 id=&quot;2-4-why-std-dynamicbitset-is-the-right-choice-here&quot;&gt;2.4 Why &lt;code&gt;std.DynamicBitSet&lt;&#x2F;code&gt; is the &lt;em&gt;right&lt;&#x2F;em&gt; choice here&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;It is in the standard library.&lt;&#x2F;strong&gt; No reader needs to chase project-local semantics to understand what &lt;code&gt;set&lt;&#x2F;code&gt;&#x2F;&lt;code&gt;isSet&lt;&#x2F;code&gt;&#x2F;&lt;code&gt;initEmpty&lt;&#x2F;code&gt; mean. The standard library type is the lingua franca; using it is a hint that nothing special is going on.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;It exposes standard, familiar operations and no cache-specific protocol.&lt;&#x2F;strong&gt; &lt;code&gt;std.DynamicBitSet&lt;&#x2F;code&gt; actually has a larger surface than this site uses (range ops, set algebra, iterators, clone), but crucially it has &lt;em&gt;no&lt;&#x2F;em&gt; &lt;code&gt;hashWithBit&lt;&#x2F;code&gt;, no virtual-mutation invariants, no &quot;bit must be currently clear&quot; precondition to violate, no SBO branch to reason about. Nothing about its API hints at the cache-probe protocol. The shared vocabulary is the win, not minimalism.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;One allocation, one deinit, simple lifetime.&lt;&#x2F;strong&gt; ~&lt;code&gt;N&#x2F;8&lt;&#x2F;code&gt; bytes via the caller&#x27;s allocator, released by a single &lt;code&gt;defer bits.deinit()&lt;&#x2F;code&gt;. SBO would be unused: the function holds exactly one bitset, ever, so an inline buffer would not save a heap allocation per clone - there are no clones.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;The three invariants compose elegantly with bit semantics.&lt;&#x2F;strong&gt; Disjoint = &lt;code&gt;assert(!bits.isSet(idx))&lt;&#x2F;code&gt; before &lt;code&gt;set&lt;&#x2F;code&gt;; in-bounds = &lt;code&gt;assert(idx &amp;lt; expected_total)&lt;&#x2F;code&gt;; complete = &lt;code&gt;count == expected_total&lt;&#x2F;code&gt;. The bitset is doing exactly the work a set membership test should do, with a tight, obvious mapping from invariant to line of code.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;2-5-why-the-custom-bitset-would-be-wrong-here&quot;&gt;2.5 Why the custom &lt;code&gt;Bitset&lt;&#x2F;code&gt; would be &lt;em&gt;wrong&lt;&#x2F;em&gt; here&lt;&#x2F;h3&gt;
&lt;p&gt;This is the more interesting direction. The custom &lt;code&gt;Bitset&lt;&#x2F;code&gt; would mechanically work - &lt;code&gt;init&lt;&#x2F;code&gt;&#x2F;&lt;code&gt;set&lt;&#x2F;code&gt;&#x2F;&lt;code&gt;isSet&lt;&#x2F;code&gt;&#x2F;&lt;code&gt;deinit&lt;&#x2F;code&gt; are present and correct.  But choosing it would be a category error:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;The custom type carries preconditions tuned to the cache protocol.&lt;&#x2F;strong&gt; &lt;code&gt;hashWithBit&lt;&#x2F;code&gt; requires the probed bit to be currently clear; the &lt;code&gt;debug.assert&lt;&#x2F;code&gt;s inside &lt;code&gt;hashWithBit&lt;&#x2F;code&gt; and &lt;code&gt;eqlWithBit&lt;&#x2F;code&gt; enforce that. None of this matters here, but importing the type into a debug-only path creates the implicit suggestion that the whole protocol is in scope.  Future maintainers reading the assertion code would have to figure out why.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;SBO is dead code on this path.&lt;&#x2F;strong&gt; The function holds exactly one bitset of size &lt;code&gt;N&lt;&#x2F;code&gt;. The inline buffer either adds 32 bytes of stack that go unused (when &lt;code&gt;N &amp;gt; 256&lt;&#x2F;code&gt;) or replaces a single &lt;code&gt;~N&#x2F;8&lt;&#x2F;code&gt;-byte allocation that nobody cares about (when &lt;code&gt;N ≤ 256&lt;&#x2F;code&gt;). The optimisation is invisible at the call site and pays nothing.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;The custom hash is unused machinery.&lt;&#x2F;strong&gt; A bitset whose &lt;code&gt;hash&lt;&#x2F;code&gt;&#x2F;&lt;code&gt;eql&lt;&#x2F;code&gt; facilities are never called is a worse fit than one that does not have them; the API surface is wider than the requirement, and unused API surface attracts misuse.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Wire compatibility with Go&#x2F;Rust does not apply.&lt;&#x2F;strong&gt; This is a Zig-only invariant check; there is no peer port doing the same check whose hash output we need to match.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The custom type was designed for a specific, demanding access pattern.  Using it where that pattern doesn&#x27;t exist would be like reaching for a B-tree to store five elements: it works, but it advertises a complexity class the data does not have.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;3-when-to-revisit-this-decision&quot;&gt;3. When to revisit this decision&lt;&#x2F;h2&gt;
&lt;p&gt;A choice this load-bearing should come with explicit triggers for re-evaluation. The custom &lt;code&gt;Bitset&lt;&#x2F;code&gt; is justified &lt;em&gt;given the current shape of the algorithm and workload&lt;&#x2F;em&gt;; if either shifts, the cost&#x2F;benefit calculation shifts with it. Concrete triggers:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;The DFS stops doing virtual-mutation probes.&lt;&#x2F;strong&gt; If &lt;code&gt;cacheContainsWithBit&lt;&#x2F;code&gt; is removed - say, the cache key changes shape, or the algorithm switches to a different pruning strategy - then &lt;code&gt;hashWithBit&lt;&#x2F;code&gt;&#x2F;&lt;code&gt;eqlWithBit&lt;&#x2F;code&gt; lose their callers, and with them the constraint that pins the hash to a specific XOR-decomposable formula.  At that point &lt;code&gt;std.DynamicBitSetUnmanaged&lt;&#x2F;code&gt; plus a thin &lt;code&gt;hash()&lt;&#x2F;code&gt; helper covers the remaining requirements. The custom type is no longer load-bearing; deleting it would be a strict simplification.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Typical histories outgrow the inline buffer.&lt;&#x2F;strong&gt; SBO is calibrated for &lt;code&gt;n_ops ≤ 256&lt;&#x2F;code&gt;. If the project&#x27;s target workloads shift to consistently larger histories - e.g., new fixtures in the few-thousand-ops range - the inline buffer becomes dead weight rather than a fast path, and the case for SBO weakens. The right response there is probably to retune &lt;code&gt;inline_cap&lt;&#x2F;code&gt; rather than abandon the custom type, but the question is worth asking.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;The cache hit rate collapses.&lt;&#x2F;strong&gt; The deferred-clone optimisation pays off in proportion to the probe-to-commit ratio. If a future model shape produces hit rates closer to 50&#x2F;50, the savings from &lt;code&gt;hashWithBit&lt;&#x2F;code&gt; shrink (though SBO continues to pay on the misses).  This is a tuning signal, not a redesign signal - but it&#x27;s a place to measure rather than assume.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Zig&#x27;s standard library grows what we built.&lt;&#x2F;strong&gt; If a future &lt;code&gt;std&lt;&#x2F;code&gt; ships a small-buffer-optimized bitset or a bitset with an algebraically-updateable hash, the custom type becomes a candidate for deletion. Until then, neither feature is in &lt;code&gt;std&lt;&#x2F;code&gt;, and external dependencies are not worth introducing for a 330-line file.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;The arena lifetime model goes away.&lt;&#x2F;strong&gt; The &quot;allocator on the call site&quot; discipline matters because every bitset in the cache is owned by a per-partition arena. If the checker moves to a different memory strategy - long-lived caches across partitions, persistent search trees, anything - the unmanaged-allocator argument weakens, and a managed type becomes more reasonable.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Symmetrically, the std-bitset choice in &lt;code&gt;assertPartitionIndependent&lt;&#x2F;code&gt; is trivially robust: it would only need to change if the function were promoted out of debug-only mode, at which point the access-pattern analysis would have to be redone from scratch.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;4-the-general-principle&quot;&gt;4. The general principle&lt;&#x2F;h2&gt;
&lt;p&gt;The two call sites in this project illustrate a discipline that is easy to state and consistently hard to follow: &lt;strong&gt;data structure choice should be driven by access pattern, not by category.&lt;&#x2F;strong&gt; &quot;Bitset&quot; is not a specification. The questions that produce a specification look like:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;How often is the structure mutated relative to how often it is read?&lt;&#x2F;li&gt;
&lt;li&gt;Is it cloned? At what rate? With what allocator?&lt;&#x2F;li&gt;
&lt;li&gt;Is it hashed or compared? Is the hash on the critical path?&lt;&#x2F;li&gt;
&lt;li&gt;Does the algorithm ask &quot;what would the structure look like if I applied operation &lt;code&gt;X&lt;&#x2F;code&gt;?&quot; without committing to &lt;code&gt;X&lt;&#x2F;code&gt;? (If so, the structure may need primitives for &lt;em&gt;virtual&lt;&#x2F;em&gt; mutation.)&lt;&#x2F;li&gt;
&lt;li&gt;What is the lifetime - one stack frame, one DFS subtree, one partition, one process?&lt;&#x2F;li&gt;
&lt;li&gt;What hardware-level effects matter - cache lines, branch prediction, allocator contention?&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;For the DFS in &lt;code&gt;checker.zig&lt;&#x2F;code&gt;, the answers point to a small custom type with SBO, an algebraically-updateable hash, deferred-clone probe primitives, and an arena-friendly allocator interface; given the constraints, that is the simplest and most controllable implementation in this codebase. For &lt;code&gt;assertPartitionIndependent&lt;&#x2F;code&gt;, the answers point to the simplest bit-of-storage primitive in the standard library.&lt;&#x2F;p&gt;
&lt;p&gt;These are not opposing aesthetics; they are the same aesthetic - &lt;em&gt;fit the tool to the work&lt;&#x2F;em&gt; - applied to different work. The fact that one site needs a finely-engineered custom type does not mean every other bitset use in the project should reach for it. The fact that the standard library has a perfectly adequate &lt;code&gt;DynamicBitSet&lt;&#x2F;code&gt; does not mean the hot path should give up SBO, an updateable hash, and virtual-mutation probing to use it - those primitives, not a generic &quot;slowdown&quot; headline, are the load-bearing pieces, and the right way to size their value is a benchmark table comparing the current code against (a) a custom &lt;code&gt;Bitset&lt;&#x2F;code&gt; without virtual-mutation probing and (b) &lt;code&gt;std.DynamicBitSetUnmanaged&lt;&#x2F;code&gt; plus clone-then-hash.&lt;&#x2F;p&gt;
&lt;p&gt;The two bitsets in this codebase exist because the two call sites have disjoint requirements. Picking one tool for both would either pessimise the algorithm that defines the project&#x27;s performance, or import a specialised type&#x27;s invariants into a debug-only assertion that has no use for them. Keeping them separate is the boring, correct answer.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Zig Allocation Patterns</title>
          <pubDate>Sun, 17 May 2026 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://debasishg.github.io/blog/zig-allocation-patterns/</link>
          <guid>https://debasishg.github.io/blog/zig-allocation-patterns/</guid>
          <description xml:base="https://debasishg.github.io/blog/zig-allocation-patterns/">&lt;h1 id=&quot;zig-allocation-patterns&quot;&gt;Zig Allocation Patterns&lt;&#x2F;h1&gt;
&lt;p&gt;A reference for the allocation and ownership patterns used in &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;debasishg&#x2F;porcupine-zig&quot;&gt;&lt;code&gt;porcupine-zig&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;, plus the broader landscape of Zig allocator idioms. Examples cite types and functions in the codebase by name so the patterns can be grounded against real code. But the discussion in this post stands on its own and does not require reading those sources.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;1-the-two-ownership-models-aware-vs-owning&quot;&gt;1. The two ownership models: aware vs. owning&lt;&#x2F;h2&gt;
&lt;p&gt;Both patterns satisfy the project rule &quot;every allocating function takes an explicit &lt;code&gt;std.mem.Allocator&lt;&#x2F;code&gt;.&quot; The split is about &lt;strong&gt;whether the type also stores a copy of that allocator inside itself.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;1-1-allocator-aware-unmanaged&quot;&gt;1.1 Allocator-aware (Unmanaged)&lt;&#x2F;h3&gt;
&lt;p&gt;The type holds &lt;strong&gt;no&lt;&#x2F;strong&gt; allocator field. Every method that allocates or frees re-receives one as a parameter, and the caller is responsible for passing the &lt;em&gt;same&lt;&#x2F;em&gt; allocator on &lt;code&gt;init&lt;&#x2F;code&gt;, &lt;code&gt;clone&lt;&#x2F;code&gt;, and &lt;code&gt;deinit&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;zig&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;var&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; bs&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; try&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt; Bitset.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;init&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt;(arena_alloc,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 256&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;defer&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt; bs.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;deinit&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt;(arena_alloc);&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt;          &#x2F;&#x2F; caller re-supplies arena_alloc&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;const&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; cp&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; try&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt; bs.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;clone&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt;(arena_alloc);&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt;  &#x2F;&#x2F; and again&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;Use it when:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Many short-lived instances share one allocator.&lt;&#x2F;strong&gt; The custom &lt;code&gt;Bitset&lt;&#x2F;code&gt; used by the checker clones constantly inside the DFS - one per cache entry, often hundreds or thousands per partition. All of them live and die under the same per-worker arena. Storing a 16-byte &lt;code&gt;std.mem.Allocator&lt;&#x2F;code&gt; (vtable pointer + state pointer) on every instance would inflate the type for zero information gain - it&#x27;s the same allocator every time.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;The type is small and SBO-sensitive.&lt;&#x2F;strong&gt; &lt;code&gt;Bitset&lt;&#x2F;code&gt; deliberately keeps a &lt;code&gt;[4]u64&lt;&#x2F;code&gt; inline buffer to dodge heap allocation for histories ≤ 256 ops.  Adding 16 bytes of allocator metadata to a struct whose whole point is staying compact would defeat the optimization.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;The instance is embedded inside another struct that already owns its own lifetime story.&lt;&#x2F;strong&gt; Putting an &lt;code&gt;ArrayListUnmanaged&lt;&#x2F;code&gt; inside a struct lets the outer struct drive &lt;code&gt;deinit&lt;&#x2F;code&gt; cleanup with whichever allocator it already has on hand.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;You want the type to be trivially copyable as a value.&lt;&#x2F;strong&gt; A &lt;code&gt;Bitset&lt;&#x2F;code&gt; can be assigned by value (with the documented caveat that the &lt;code&gt;heap&lt;&#x2F;code&gt; slice is a shallow alias - both copies will point at the same heap buffer). A managed type with an embedded allocator complicates the question of who owns what after a copy.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;The standard library&#x27;s &lt;code&gt;ArrayListUnmanaged&lt;&#x2F;code&gt;, &lt;code&gt;HashMapUnmanaged&lt;&#x2F;code&gt;, &lt;code&gt;BufMap&lt;&#x2F;code&gt; family all follow this rule - and as of Zig 0.14+ the unmanaged variants are the default; the managed wrappers were largely removed.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;1-2-allocator-owning-managed&quot;&gt;1.2 Allocator-owning (Managed)&lt;&#x2F;h3&gt;
&lt;p&gt;The type stores its allocator at construction time and exposes a parameterless &lt;code&gt;deinit()&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;zig&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;var&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; arena&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; try&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; NodeArenaOf&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt;(M).&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;fromEntries&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt;(gpa, entries);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;defer&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt; arena.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;deinit&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;();&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt;   &#x2F;&#x2F; no allocator argument needed&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;Use it when:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;There&#x27;s exactly one (or a small handful of) long-lived instances.&lt;&#x2F;strong&gt; The checker&#x27;s &lt;code&gt;NodeArena&lt;&#x2F;code&gt; is created once per partition, holds one bulk &lt;code&gt;[]Node&lt;&#x2F;code&gt; allocation, and has one well-defined teardown site. The 16-byte cost is paid once; the duplication argument doesn&#x27;t apply.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;The deinit site is far from the init site.&lt;&#x2F;strong&gt; When a struct travels through several layers of code (returned from a builder, stashed in a context, eventually torn down by a &lt;code&gt;defer&lt;&#x2F;code&gt; near the top), threading the right allocator back to the deinit call is a footgun. Storing it eliminates the question &quot;which allocator did this come from?&quot; entirely.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;The deinit shape needs to be compatible with &lt;code&gt;defer&lt;&#x2F;code&gt; without a closure.&lt;&#x2F;strong&gt; &lt;code&gt;defer arena.deinit()&lt;&#x2F;code&gt; is a one-liner; &lt;code&gt;defer arena.deinit(...)&lt;&#x2F;code&gt; requires the allocator to still be in scope and unambiguous.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;The owned resource is non-trivial enough that mismatched-allocator bugs would be expensive.&lt;&#x2F;strong&gt; Freeing a &lt;code&gt;[]Node&lt;&#x2F;code&gt; with the wrong allocator is undefined behavior; storing the allocator removes the chance.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Standard-library precedent: &lt;code&gt;std.heap.ArenaAllocator&lt;&#x2F;code&gt; itself is managed (it stores the child allocator). &lt;code&gt;std.process.ArgIterator&lt;&#x2F;code&gt; on most platforms stores its allocator. &lt;code&gt;std.Build&lt;&#x2F;code&gt; stores allocators throughout.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;1-3-the-mental-model-cost-vs-risk&quot;&gt;1.3 The mental model: cost vs. risk&lt;&#x2F;h3&gt;
&lt;p&gt;The choice reduces to a tradeoff between two costs:&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Cost&lt;&#x2F;th&gt;&lt;th&gt;Aware&lt;&#x2F;th&gt;&lt;th&gt;Owning&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Per-instance memory (allocator field)&lt;&#x2F;td&gt;&lt;td&gt;0 bytes&lt;&#x2F;td&gt;&lt;td&gt;16 bytes&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Risk of mismatched allocator at deinit&lt;&#x2F;td&gt;&lt;td&gt;Caller&#x27;s burden&lt;&#x2F;td&gt;&lt;td&gt;Eliminated&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Trivially copyable as a value&lt;&#x2F;td&gt;&lt;td&gt;Yes (with caveats)&lt;&#x2F;td&gt;&lt;td&gt;No (semantic gotcha)&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Deinit ergonomics&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;x.deinit(alloc)&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;x.deinit()&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;Aware wins on memory and copy semantics. Owning wins on safety and ergonomics.  Population size and lifetime usually decide it: &lt;strong&gt;many short-lived → aware, few long-lived → owning.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;2-other-zig-allocation-patterns&quot;&gt;2. Other Zig allocation patterns&lt;&#x2F;h2&gt;
&lt;p&gt;The aware&#x2F;owning axis is just one dimension. Several other patterns are in active use across the standard library and this codebase.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;2-1-arena-allocation-lifetime-bundled&quot;&gt;2.1 Arena allocation (lifetime-bundled)&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;code&gt;std.heap.ArenaAllocator&lt;&#x2F;code&gt; - many objects share one allocator, and freeing happens &lt;em&gt;only&lt;&#x2F;em&gt; in bulk (&lt;code&gt;deinit&lt;&#x2F;code&gt; or &lt;code&gt;reset(.retain_capacity)&lt;&#x2F;code&gt;). Per-object &lt;code&gt;free&lt;&#x2F;code&gt; is a no-op. Used pervasively here: &lt;code&gt;WorkerCtx.run&lt;&#x2F;code&gt; holds one arena per worker, resets it between partitions, throws it away on exit. The arena itself is allocator-owning (it stores its child allocator); the things it allocates for are usually allocator-aware (&lt;code&gt;Bitset&lt;&#x2F;code&gt;, hashmaps).&lt;&#x2F;p&gt;
&lt;p&gt;This is orthogonal to aware&#x2F;owning: an arena is a &lt;em&gt;strategy&lt;&#x2F;em&gt;, while aware&#x2F;owning is a &lt;em&gt;contract&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;When arenas are wrong:&lt;&#x2F;strong&gt; when individual objects need to outlive each other in unpredictable orders, or when objects hold non-memory resources (file handles, locks) that need explicit release. Arenas reclaim memory only.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;2-2-fixed-buffer-stack-backed-allocators&quot;&gt;2.2 Fixed-buffer &#x2F; stack-backed allocators&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;code&gt;std.heap.FixedBufferAllocator&lt;&#x2F;code&gt; wraps a &lt;code&gt;[N]u8&lt;&#x2F;code&gt; and bump-allocates inside it.  No heap touch at all. Useful for parsers, formatters, comptime-bounded scratch - not used here, but the SBO pattern in &lt;code&gt;Bitset&lt;&#x2F;code&gt; is the manual cousin: a &lt;code&gt;[4]u64&lt;&#x2F;code&gt; inline buffer that dodges allocation entirely until you exceed it.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;FixedBufferAllocator&lt;&#x2F;code&gt; returns &lt;code&gt;error.OutOfMemory&lt;&#x2F;code&gt; when the buffer is exhausted, so callers still need to handle the fallible path; the win is no syscall, no heap fragmentation, and no thread-safety concerns.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;2-3-allocator-free-types&quot;&gt;2.3 Allocator-free types&lt;&#x2F;h3&gt;
&lt;p&gt;Types that allocate nothing: pure value types or types backed by a caller-provided slice. &lt;code&gt;Bitset.data()&lt;&#x2F;code&gt; is a borrowed view over the active chunks; the project&#x27;s &lt;code&gt;Operation&lt;&#x2F;code&gt;, &lt;code&gt;Event&lt;&#x2F;code&gt;, and &lt;code&gt;CheckResult&lt;&#x2F;code&gt; types are POD.  No &lt;code&gt;init&lt;&#x2F;code&gt;&#x2F;&lt;code&gt;deinit&lt;&#x2F;code&gt; ceremony, no allocator threading. Always prefer this when feasible - the best allocation is no allocation.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;2-4-caller-provided-buffer-byo-storage&quot;&gt;2.4 Caller-provided buffer (&quot;BYO storage&quot;)&lt;&#x2F;h3&gt;
&lt;p&gt;The function takes a &lt;code&gt;[]T&lt;&#x2F;code&gt; from the caller and writes into it; ownership stays with the caller. &lt;code&gt;std.fmt.bufPrint&lt;&#x2F;code&gt;, &lt;code&gt;std.fs.File.read&lt;&#x2F;code&gt;. The function never allocates. Useful when the caller already has a buffer or wants tight control. &lt;code&gt;Bitset.dataMut()&lt;&#x2F;code&gt; exposes this shape on the read side.&lt;&#x2F;p&gt;
&lt;p&gt;The pattern composes well with fixed buffers: a caller can stack-allocate a &lt;code&gt;[1024]u8&lt;&#x2F;code&gt;, hand a slice of it to a library, and never touch the heap.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;2-5-bulk-single-allocation-containers-the-index-arena&quot;&gt;2.5 Bulk single-allocation containers (the &quot;index arena&quot;)&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;code&gt;NodeArenaOf&lt;&#x2F;code&gt; is itself a pattern: instead of N small &lt;code&gt;Node&lt;&#x2F;code&gt; allocations linked by pointers, do &lt;strong&gt;one&lt;&#x2F;strong&gt; &lt;code&gt;alloc([]Node)&lt;&#x2F;code&gt; and link nodes by &lt;code&gt;u32&lt;&#x2F;code&gt; indices. Halves per-node footprint, makes free a single call, plays nicely with arena resets, and keeps everything cache-local. A &lt;code&gt;none_ref = std.math.maxInt(u32)&lt;&#x2F;code&gt; sentinel replaces &lt;code&gt;?u32&lt;&#x2F;code&gt; at zero cost - &lt;code&gt;u32&lt;&#x2F;code&gt; and &lt;code&gt;?u32&lt;&#x2F;code&gt; have different sizes in Zig (the latter carries a discriminator), so picking a value out of the valid range and treating it as &quot;null&quot; recovers the tighter packing while keeping the semantics.&lt;&#x2F;p&gt;
&lt;p&gt;This is closer to a layout pattern than an allocator pattern, but it exists &lt;em&gt;because&lt;&#x2F;em&gt; of how Zig encourages explicit allocator thinking. In a language that hides allocation, a pointer-linked list and an index-linked list look the same; in Zig the cost difference is right in the source.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;2-6-two-phase-reservation-capacity-hints-assume-capacity&quot;&gt;2.6 Two-phase reservation (capacity hints + assume-capacity)&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;code&gt;ensureTotalCapacity&lt;&#x2F;code&gt; (fallible) followed by &lt;code&gt;appendAssumeCapacity&lt;&#x2F;code&gt; (infallible). Lets you front-load OOM into one reservation and then write a tight loop that can&#x27;t fail. The codebase uses it where the final size is known up front - for example, building the per-partition entry array and op-id list inside &lt;code&gt;checkOperations&lt;&#x2F;code&gt;, and the event-renumbering loop in the model layer.&lt;&#x2F;p&gt;
&lt;p&gt;This is allocator-discipline rather than ownership, but it&#x27;s a distinct idiom: it splits &quot;can this fail?&quot; from &quot;will this run?&quot;, which composes better with &lt;code&gt;errdefer&lt;&#x2F;code&gt; than a sequence of fallible appends.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;2-7-scratch-two-allocator-split&quot;&gt;2.7 Scratch &#x2F; two-allocator split&lt;&#x2F;h3&gt;
&lt;p&gt;Pass two allocators: one for the long-lived output, one for the temporary working set. Useful whenever a function builds a result via a transient hashmap or buffer that is discarded before return - e.g., a function that renumbers events through an id-remap hashmap and then emits a new slice: the slice must outlive the call, the hashmap must not.&lt;&#x2F;p&gt;
&lt;p&gt;The shape is &lt;code&gt;fn foo(out_alloc: Allocator, scratch: Allocator, ...)&lt;&#x2F;code&gt;. Callers who have an arena handy pass it as &lt;code&gt;scratch&lt;&#x2F;code&gt;; callers who don&#x27;t can pass the same allocator to both arguments and pay the per-allocation cost. The win is that callers who &lt;em&gt;do&lt;&#x2F;em&gt; have an arena get bulk reclamation of the scratch state for free, without the function having to know an arena exists.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;2-8-failing-allocator-testing-wrappers&quot;&gt;2.8 Failing allocator &#x2F; testing wrappers&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;code&gt;std.testing.allocator&lt;&#x2F;code&gt; (leak detector), &lt;code&gt;std.testing.FailingAllocator&lt;&#x2F;code&gt; (OOM injection at the Nth allocation), &lt;code&gt;std.heap.GeneralPurposeAllocator(.{})&lt;&#x2F;code&gt; with &lt;code&gt;.retain_metadata = true&lt;&#x2F;code&gt;. These wrap any other allocator and observe its calls. Their value evaporates if any code on the hot path bypasses the allocator the test handed in - for instance, a worker that hardcodes &lt;code&gt;std.heap.page_allocator&lt;&#x2F;code&gt; instead of taking the allocator from its parent will silently skip leak detection for everything it allocates. The discipline pairs with §3.1 below: every layer must accept an allocator parameter, even when the &quot;obvious&quot; choice would be a global.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;FailingAllocator&lt;&#x2F;code&gt; is the standard way to test OOM paths: configure it to fail on the Nth allocation, run the function, assert it returns &lt;code&gt;error.OutOfMemory&lt;&#x2F;code&gt; cleanly with no leaks. The &lt;code&gt;HashMapKvModel&lt;&#x2F;code&gt; OOM tests in this project use exactly this pattern to exercise the &lt;code&gt;errdefer&lt;&#x2F;code&gt; unwinding inside &lt;code&gt;step&lt;&#x2F;code&gt; and &lt;code&gt;clone&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;2-9-concrete-backing-allocators-page-c-smp&quot;&gt;2.9 Concrete backing allocators (page &#x2F; C &#x2F; smp)&lt;&#x2F;h3&gt;
&lt;p&gt;The patterns above are &lt;em&gt;strategies&lt;&#x2F;em&gt;; at the bottom of the stack sits a concrete allocator that actually returns memory. Three from the standard library show up in real code, each picked along a different axis:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;std.heap.page_allocator&lt;&#x2F;code&gt;&lt;&#x2F;strong&gt; goes straight to &lt;code&gt;mmap&lt;&#x2F;code&gt; &#x2F; &lt;code&gt;VirtualAlloc&lt;&#x2F;code&gt;.  No fragmentation tracking, allocations rounded up to page size (typically 4 KiB). Cheap for one big request, expensive per call. Almost always wrong for small, frequent requests - that&#x27;s what &lt;code&gt;GeneralPurposeAllocator&lt;&#x2F;code&gt; is for - but it shines as the backing store for arenas: the arena amortizes the page-grain cost across many small client requests, so &lt;code&gt;page_allocator&lt;&#x2F;code&gt; is only called when the arena actually grows. The per-worker arenas in this codebase use it for exactly that reason.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;std.heap.c_allocator&lt;&#x2F;code&gt;&lt;&#x2F;strong&gt; calls &lt;code&gt;malloc&lt;&#x2F;code&gt; &#x2F; &lt;code&gt;free&lt;&#x2F;code&gt;. Available only when linking libc. Useful for FFI scenarios where Zig-allocated memory crosses into C code, since C code expects to call &lt;code&gt;free&lt;&#x2F;code&gt; on it. Outside FFI, no edge over &lt;code&gt;GeneralPurposeAllocator&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;std.heap.smp_allocator&lt;&#x2F;code&gt;&lt;&#x2F;strong&gt; (Zig 0.14+) - a thread-safe, general-purpose allocator suitable for multi-threaded code. The natural fit for &quot;I have N worker threads and I want each to be free to allocate without coordinating&quot;: callers can pass &lt;code&gt;smp_allocator&lt;&#x2F;code&gt; directly without wrapping their own GPA in a thread-safe shim. In this project&#x27;s parallel partition dispatch, the worker threads each hold their own arena, but the &lt;em&gt;backing&lt;&#x2F;em&gt; allocator for those arenas needs to be safe to call concurrently - that&#x27;s the slot &lt;code&gt;smp_allocator&lt;&#x2F;code&gt; fills.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;3-anti-patterns-and-pitfalls&quot;&gt;3. Anti-patterns and pitfalls&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;3-1-hidden-global-allocators&quot;&gt;3.1 Hidden global allocators&lt;&#x2F;h3&gt;
&lt;p&gt;Resist the urge to define &lt;code&gt;const gpa = std.heap.page_allocator;&lt;&#x2F;code&gt; at module scope and reach for it from every function. The whole point of Zig&#x27;s explicit-allocator convention is that &lt;em&gt;the caller&lt;&#x2F;em&gt; decides allocation strategy: arena, GPA, fixed buffer, leak-detecting test wrapper. A hidden global silently defeats all of those.&lt;&#x2F;p&gt;
&lt;p&gt;The one defensible exception is a top-level &lt;code&gt;main&lt;&#x2F;code&gt; that picks an allocator and threads it down - but even that is one decision, not a pattern.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;3-2-mismatched-allocators-on-aware-types&quot;&gt;3.2 Mismatched allocators on aware types&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;code&gt;Bitset.deinit(other_alloc)&lt;&#x2F;code&gt; when &lt;code&gt;init&lt;&#x2F;code&gt; ran with &lt;code&gt;arena_alloc&lt;&#x2F;code&gt; is undefined behavior. The aware contract puts the burden on the caller, and Zig has no compile-time check that catches it. Mitigations:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Pair &lt;code&gt;defer&lt;&#x2F;code&gt; with the allocation site so they&#x27;re visually adjacent.&lt;&#x2F;li&gt;
&lt;li&gt;Don&#x27;t pass aware types across module boundaries without documenting the allocator contract.&lt;&#x2F;li&gt;
&lt;li&gt;When in doubt, prefer owning: the cost is 16 bytes, the benefit is the whole class of bug going away.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;3-3-shallow-copies-of-aware-types-with-heap-state&quot;&gt;3.3 Shallow copies of aware types with heap state&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;code&gt;Bitset&lt;&#x2F;code&gt; is a value type; &lt;code&gt;var b2 = b1;&lt;&#x2F;code&gt; shares the &lt;code&gt;heap&lt;&#x2F;code&gt; slice. Freeing either invalidates the other. The codebase deliberately holds bitsets through pointers - for example, iterating with &lt;code&gt;for (entries.items) |*e|&lt;&#x2F;code&gt; rather than &lt;code&gt;|e|&lt;&#x2F;code&gt; - to avoid making an aliased copy on every loop step.  &lt;code&gt;Bitset&lt;&#x2F;code&gt;&#x27;s doc comment flags the hazard; without that comment, the bug class is invisible at the call site.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;3-4-per-element-deinit-under-an-arena&quot;&gt;3.4 Per-element deinit under an arena&lt;&#x2F;h3&gt;
&lt;p&gt;If an arena is going to reclaim everything in one call, walking each element and &lt;code&gt;free&lt;&#x2F;code&gt;ing it first is pure waste - and worse, it&#x27;s misleading documentation: a reader sees the loop and assumes the elements have non-trivial cleanup, which forces them to reason about ordering, partial failure, and reentrancy. The fix is either to delete the loop and document the arena assumption at the allocation site, or - if the type genuinely might hold non-memory resources like file handles - gate the loop behind a comptime marker on the type so the dead version is removed in builds where the elements really are arena-safe.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;3-5-storing-an-allocator-inside-a-thread-shared-struct&quot;&gt;3.5 Storing an allocator inside a thread-shared struct&lt;&#x2F;h3&gt;
&lt;p&gt;If multiple threads share a struct that owns its allocator, and the allocator isn&#x27;t thread-safe (&lt;code&gt;GeneralPurposeAllocator(.{})&lt;&#x2F;code&gt; defaults are single-threaded), every allocation is a race. Either pick a thread-safe allocator (&lt;code&gt;smp_allocator&lt;&#x2F;code&gt;, &lt;code&gt;c_allocator&lt;&#x2F;code&gt;) or give each thread its own arena, as &lt;code&gt;WorkerCtx&lt;&#x2F;code&gt; does.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;3-6-returning-slices-from-a-function-whose-allocator-goes-out-of-scope&quot;&gt;3.6 Returning slices from a function whose allocator goes out of scope&lt;&#x2F;h3&gt;
&lt;p&gt;The classic: a function creates a &lt;code&gt;FixedBufferAllocator&lt;&#x2F;code&gt; on its stack frame and returns a slice allocated from it. The frame unwinds, the buffer is gone, the slice is dangling. Either return ownership with &lt;code&gt;toOwnedSlice&lt;&#x2F;code&gt; (backed by a heap allocator that outlives the call), or have the caller pass in the buffer.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;4-quick-decision-table&quot;&gt;4. Quick decision table&lt;&#x2F;h2&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Pattern&lt;&#x2F;th&gt;&lt;th&gt;When it fits&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Allocator-aware (Unmanaged)&lt;&#x2F;td&gt;&lt;td&gt;Many small instances; SBO-sensitive; embedded in a parent that drives deinit&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Allocator-owning (Managed)&lt;&#x2F;td&gt;&lt;td&gt;Few long-lived owners; deinit site distant from init; mismatch is dangerous&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Arena&lt;&#x2F;td&gt;&lt;td&gt;Many small lifetimes coterminous with a single phase&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Fixed-buffer &#x2F; SBO&lt;&#x2F;td&gt;&lt;td&gt;Bounded size known at comptime or by domain heuristic&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Allocator-free&lt;&#x2F;td&gt;&lt;td&gt;POD &#x2F; borrowed views&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;BYO buffer&lt;&#x2F;td&gt;&lt;td&gt;Caller already has storage; library doesn&#x27;t need policy&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Index-arena (one bulk alloc)&lt;&#x2F;td&gt;&lt;td&gt;Graphs &#x2F; linked structures of homogeneous nodes&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Two-phase reserve + assume&lt;&#x2F;td&gt;&lt;td&gt;Hot-loop appends where OOM should be front-loaded&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Scratch + output split&lt;&#x2F;td&gt;&lt;td&gt;Function with transient working set + persistent result&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Failing&#x2F;leak-detecting wrapper&lt;&#x2F;td&gt;&lt;td&gt;Tests; OOM-path coverage&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Page allocator&lt;&#x2F;td&gt;&lt;td&gt;Backing store for arenas; rare direct use&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;C allocator&lt;&#x2F;td&gt;&lt;td&gt;FFI boundaries with C code&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Smp allocator&lt;&#x2F;td&gt;&lt;td&gt;Multi-threaded code without a per-thread arena&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;5-how-the-patterns-compose-in-porcupine-zig&quot;&gt;5. How the patterns compose in &lt;code&gt;porcupine-zig&lt;&#x2F;code&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;A single &lt;code&gt;checkOperations&lt;&#x2F;code&gt; call exercises most of the table:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Caller&#x27;s allocator&lt;&#x2F;strong&gt; (any) is handed in for the entry array and partition slices - the long-lived outputs that survive the call.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Per-worker arena&lt;&#x2F;strong&gt; wraps &lt;code&gt;page_allocator&lt;&#x2F;code&gt; and serves every DFS-internal allocation: bitsets, hashmap nodes, cloned states, linearization records.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Allocator-aware &lt;code&gt;Bitset&lt;&#x2F;code&gt;&lt;&#x2F;strong&gt; is allocated inside the arena, cloned thousands of times during DFS, and never individually freed - the arena reclaims it all on &lt;code&gt;reset&lt;&#x2F;code&gt; between partitions.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Allocator-owning &lt;code&gt;NodeArena&lt;&#x2F;code&gt;&lt;&#x2F;strong&gt; holds the doubly-linked-list of call&#x2F;return nodes for the whole partition, allocated from the caller&#x27;s allocator (one bulk alloc) and freed once at the end.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Two-phase reserve + assume&lt;&#x2F;strong&gt; in &lt;code&gt;checkOperations&lt;&#x2F;code&gt; and &lt;code&gt;dedupe&lt;&#x2F;code&gt; pre-sizes the entry array and op-id list so the inner loop can&#x27;t OOM.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Index arena&lt;&#x2F;strong&gt; layout in &lt;code&gt;NodeArenaOf&lt;&#x2F;code&gt; keeps the linked-list traversal cache-friendly with &lt;code&gt;u32&lt;&#x2F;code&gt; links and a &lt;code&gt;none_ref&lt;&#x2F;code&gt; sentinel.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Allocator-free types&lt;&#x2F;strong&gt; (&lt;code&gt;Operation&lt;&#x2F;code&gt;, &lt;code&gt;Event&lt;&#x2F;code&gt;, &lt;code&gt;CheckResult&lt;&#x2F;code&gt;) flow through the API boundary without any allocation ceremony.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Each pattern is doing the job it&#x27;s best at, and the boundaries between them match natural lifetime boundaries in the algorithm. That&#x27;s the goal: pattern selection should fall out of &quot;what lives how long?&quot; rather than being imposed top-down.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;6-further-reading&quot;&gt;6. Further reading&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;Zig standard library source: &lt;code&gt;lib&#x2F;std&#x2F;heap.zig&lt;&#x2F;code&gt; and &lt;code&gt;lib&#x2F;std&#x2F;mem&#x2F;Allocator.zig&lt;&#x2F;code&gt; for the canonical implementations of every allocator mentioned here. The &lt;code&gt;Allocator&lt;&#x2F;code&gt; interface in particular is worth reading end-to-end - it is short, and seeing how the vtable is laid out makes the &quot;16 bytes&quot; cost in §1 concrete.&lt;&#x2F;li&gt;
&lt;li&gt;The Zig language reference&#x27;s &quot;Memory&quot; chapter, for the rationale behind explicit-allocator passing as a language design choice.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</description>
      </item>
      <item>
          <title>Why Zig’s Io Feels Like an Effect System (Without Being One)</title>
          <pubDate>Mon, 11 May 2026 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://debasishg.github.io/blog/effects/</link>
          <guid>https://debasishg.github.io/blog/effects/</guid>
          <description xml:base="https://debasishg.github.io/blog/effects/">&lt;h1 id=&quot;why-zig-s-io-feels-like-an-effect-system-without-being-one&quot;&gt;Why Zig’s &lt;code&gt;Io&lt;&#x2F;code&gt; Feels Like an Effect System (Without Being One)&lt;&#x2F;h1&gt;
&lt;h2 id=&quot;context-what-does-effect-even-mean&quot;&gt;Context: What does &quot;effect&quot; even mean?&lt;&#x2F;h2&gt;
&lt;p&gt;The word is overloaded, and most arguments about whether something &quot;is&quot; an effect collapse the moment you fix a sense. At least four are in play:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;A &lt;strong&gt;semantic side effect&lt;&#x2F;strong&gt; - reading a file, mutating memory, throwing.&lt;&#x2F;li&gt;
&lt;li&gt;A &lt;strong&gt;type-level effect annotation&lt;&#x2F;strong&gt; - Koka-style effect rows, where the function type itself names which effects may occur.&lt;&#x2F;li&gt;
&lt;li&gt;An &lt;strong&gt;algebraic operation plus handler&lt;&#x2F;strong&gt; - a &lt;code&gt;perform&lt;&#x2F;code&gt;&#x2F;&lt;code&gt;handle&lt;&#x2F;code&gt; mechanism that reifies the continuation and lets a handler decide whether, when, or how often to resume it (OCaml 5, Eff, Koka).&lt;&#x2F;li&gt;
&lt;li&gt;A &lt;strong&gt;capability value&lt;&#x2F;strong&gt; - an explicit value that grants the authority to perform an action, threaded through call sites (object-capability style).&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;These can stack but they are independent. Koka has all four. OCaml 5 has (1)+(3) but not (2): its manual is explicit that unhandled effects raise &lt;code&gt;Effect.Unhandled&lt;&#x2F;code&gt; at runtime, with no static effect safety. Zig &lt;code&gt;Io&lt;&#x2F;code&gt; is squarely (1)+(4).&lt;&#x2F;p&gt;
&lt;p&gt;When this document says &quot;effect&quot; without qualification, it usually means sense (3) - the algebraic, handler-based shape that the term carries in PL theory.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;the-opening-question-is-io-an-effect&quot;&gt;The opening question: is &lt;code&gt;Io&lt;&#x2F;code&gt; an effect?&lt;&#x2F;h2&gt;
&lt;p&gt;Zig 0.16 (April 2026) introduced an &lt;code&gt;Io&lt;&#x2F;code&gt; interface that you thread through any function that performs I&#x2F;O. The release notes phrase it bluntly: &quot;all input and output functionality requires being passed an &lt;code&gt;Io&lt;&#x2F;code&gt; instance.&quot; The threaded implementation (&lt;code&gt;Io.Threaded&lt;&#x2F;code&gt;) is feature-complete today; evented, &lt;code&gt;io_uring&lt;&#x2F;code&gt;, &lt;code&gt;kqueue&lt;&#x2F;code&gt;, and Grand-Central-Dispatch backends range from work-in-progress to proof-of-concept; &lt;code&gt;std.testing.io&lt;&#x2F;code&gt; is the recommended fake for tests.&lt;&#x2F;p&gt;
&lt;p&gt;A typical signature looks like:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;zig&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-function&quot;&gt;fn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; readConfig&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt;(io: Io, path: []&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;const&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-type&quot;&gt; u8&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;!&lt;&#x2F;span&gt;&lt;span&gt;[]&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-type&quot;&gt;u8&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The same function body is intended to work across different &lt;code&gt;Io&lt;&#x2F;code&gt; implementations - the threaded backend today, evented or platform-specific backends as they mature - without splitting code into &lt;code&gt;async&lt;&#x2F;code&gt; and &lt;code&gt;sync&lt;&#x2F;code&gt; variants. This is the same engineering payoff that effect systems advertise: one body, many interpretations, no function colouring.&lt;&#x2F;p&gt;
&lt;p&gt;So: is it an effect?&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Short answer.&lt;&#x2F;strong&gt; Not in the algebraic-effect sense (3), but it is exactly an effect in the capability sense (4), and it captures most of the engineering benefits people normally reach for an effect system to get.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;why-it-is-not-an-algebraic-effect-system&quot;&gt;Why it is not an algebraic-effect system&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;No effect row for I&#x2F;O.&lt;&#x2F;strong&gt; Unlike Koka-style effect-typed systems, Zig does not attach an I&#x2F;O effect row to the return type. Two functions that do wildly different I&#x2F;O have indistinguishable signatures apart from &quot;they both take an &lt;code&gt;Io&lt;&#x2F;code&gt;.&quot;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;No effect operations plus handlers.&lt;&#x2F;strong&gt; There is no &lt;code&gt;perform&lt;&#x2F;code&gt;&#x2F;&lt;code&gt;handle&lt;&#x2F;code&gt; mechanism that captures a delimited continuation and lets a handler decide whether, when, or how often to resume it. You select an interpretation by &lt;em&gt;passing a different &lt;code&gt;Io&lt;&#x2F;code&gt; value&lt;&#x2F;em&gt;, not by handling an operation.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Ordinary interface dispatch.&lt;&#x2F;strong&gt; &lt;code&gt;Io&lt;&#x2F;code&gt; is a value-level interface, not a statically elaborated effect operation.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The absence of effect rows is not by itself what disqualifies Zig - OCaml 5 has algebraic-effect handlers without effect rows, and we still call those algebraic effects. The decisive missing piece in Zig is sense (3): operations plus handlers that capture a continuation.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;why-it-nevertheless-feels-effect-like&quot;&gt;Why it nevertheless feels effect-like&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;It is an explicit &lt;strong&gt;capability&lt;&#x2F;strong&gt; threaded through call sites - the same pattern as Zig&#x27;s &lt;code&gt;Allocator&lt;&#x2F;code&gt;. To do I&#x2F;O, you must be handed the right to do I&#x2F;O.&lt;&#x2F;li&gt;
&lt;li&gt;It decouples &lt;em&gt;description&lt;&#x2F;em&gt; (call &lt;code&gt;io.read(...)&lt;&#x2F;code&gt;) from &lt;em&gt;interpretation&lt;&#x2F;em&gt; (threaded, evented, fake). That is the same separation algebraic effects give you, achieved with an interface value instead of a handler.&lt;&#x2F;li&gt;
&lt;li&gt;It avoids &lt;code&gt;Future[T]&lt;&#x2F;code&gt; &#x2F; &lt;code&gt;async fn&lt;&#x2F;code&gt; colouring: the same function body can be driven under different I&#x2F;O strategies. It does &lt;strong&gt;not&lt;&#x2F;strong&gt; eliminate all visible plumbing - idiomatic Zig still accepts an &lt;code&gt;Io&lt;&#x2F;code&gt; parameter or stores it in a context struct - but it does eliminate the language-level split into separate function categories.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The accurate label is &lt;strong&gt;capability-passing I&#x2F;O&lt;&#x2F;strong&gt;: a &lt;em&gt;value-level&lt;&#x2F;em&gt; effect, in the same family as Scala&#x27;s ZIO environment, OCaml&#x27;s &lt;code&gt;Eio&lt;&#x2F;code&gt; (deliberately designed before OCaml 5 effects landed and still capability-based), or the Reader-monad-of-IO pattern. Andrew Kelley has been explicit that Zig is not adopting an effect system; &lt;code&gt;Io&lt;&#x2F;code&gt; is an interface that gives you most of the engineering benefits of one without the type-system machinery.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;unpacking-the-technical-claim&quot;&gt;Unpacking the technical claim&lt;&#x2F;h2&gt;
&lt;p&gt;The previous section said &lt;code&gt;Io&lt;&#x2F;code&gt; &quot;is just a parameter&quot; with no propagation up the call graph. Two distinct claims, worth separating.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;the-function-signature-doesn-t-carry-an-effect-row&quot;&gt;&quot;The function signature doesn&#x27;t carry an effect row&quot;&lt;&#x2F;h3&gt;
&lt;p&gt;In an effect-typed system (Koka), the &lt;em&gt;type&lt;&#x2F;em&gt; of a function lists the effects it can perform:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;fun read_config() : &amp;lt;io, exn&amp;gt; string&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;That &lt;code&gt;&amp;lt;io, exn&amp;gt;&lt;&#x2F;code&gt; is the &lt;strong&gt;effect row&lt;&#x2F;strong&gt; - a set of effect labels attached to the return type. If &lt;code&gt;read_config&lt;&#x2F;code&gt; calls something that does &lt;code&gt;&amp;lt;net&amp;gt;&lt;&#x2F;code&gt;, the compiler forces you to either &lt;em&gt;handle&lt;&#x2F;em&gt; &lt;code&gt;net&lt;&#x2F;code&gt; inside &lt;code&gt;read_config&lt;&#x2F;code&gt; or &lt;em&gt;widen&lt;&#x2F;em&gt; its row to &lt;code&gt;&amp;lt;io, exn, net&amp;gt;&lt;&#x2F;code&gt;. Effects compose by union and are visible in every signature.&lt;&#x2F;p&gt;
&lt;p&gt;Zig&#x27;s &lt;code&gt;Io&lt;&#x2F;code&gt; has nothing like that. The signature is just:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;zig&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-function&quot;&gt;fn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; readConfig&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt;(io: Io, path: []&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;const&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-type&quot;&gt; u8&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;!&lt;&#x2F;span&gt;&lt;span&gt;[]&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-type&quot;&gt;u8&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The fact that it does I&#x2F;O is encoded as a &lt;em&gt;value&lt;&#x2F;em&gt; (&lt;code&gt;io: Io&lt;&#x2F;code&gt;), not as a tag on the return type.&lt;&#x2F;p&gt;
&lt;p&gt;A note in passing: Zig already &lt;em&gt;does&lt;&#x2F;em&gt; track one effect-shaped thing in the type - errors, via error unions (&lt;code&gt;!T&lt;&#x2F;code&gt;). The example above uses &lt;code&gt;![]u8&lt;&#x2F;code&gt;. So the claim is not &quot;Zig tracks nothing effect-like.&quot; The narrower claim is that &lt;strong&gt;I&#x2F;O authority&lt;&#x2F;strong&gt; specifically is not tracked as a row or a capture set.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;the-compiler-doesn-t-enforce-that-i-o-doing-code-propagates-a-marker-upward&quot;&gt;&quot;The compiler doesn&#x27;t enforce that I&#x2F;O-doing code propagates a marker upward&quot;&lt;&#x2F;h3&gt;
&lt;p&gt;In an effect-typed system, effects are &lt;em&gt;contagious&lt;&#x2F;em&gt; through the type checker:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;fun a() : &amp;lt;io&amp;gt; ()        &#x2F;&#x2F; does I&#x2F;O&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;fun b() : &amp;lt;&amp;gt; ()  = a()   &#x2F;&#x2F; ERROR — b&amp;#39;s row is empty but it called something with &amp;lt;io&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The fix is forced: widen &lt;code&gt;b&lt;&#x2F;code&gt;&#x27;s row, or install a handler. You cannot silently launder an effect away.&lt;&#x2F;p&gt;
&lt;p&gt;In Zig, nothing propagates. If you have an &lt;code&gt;Io&lt;&#x2F;code&gt; in scope (e.g. in a struct field), you can call I&#x2F;O from a function whose signature mentions no &lt;code&gt;Io&lt;&#x2F;code&gt; at all:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;zig&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;const&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; std&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function&quot;&gt; @import&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;std&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;const&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; Io&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt; std.Io;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;const&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; Logger&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; struct&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt;    io: Io,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-function&quot;&gt;    fn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; log&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt;(self:&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; *&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt;Logger, msg: []&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;const&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-type&quot;&gt; u8&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;!&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-type&quot;&gt;void&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-comment&quot;&gt;        &#x2F;&#x2F; `self.io` is the I&#x2F;O capability; the Writer is obtained from it.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        var&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; stderr&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt; self.io.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;stderr&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;();                                                         &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        try&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt; stderr.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;writeAll&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt;(msg);&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; &#x2F;&#x2F; does I&#x2F;O&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;};&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-function&quot;&gt;fn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; pureLooking&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt;(l:&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; *&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;Logger) &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;!&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-type&quot;&gt;void&lt;&#x2F;span&gt;&lt;span&gt; {  &lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt;&#x2F;&#x2F; signature gives no hint&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    try&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt; l.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;hi&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;);&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt;                &#x2F;&#x2F; …yet this does I&#x2F;O&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;pureLooking&lt;&#x2F;code&gt;&#x27;s signature says nothing about I&#x2F;O. The compiler is fine with this. The &quot;I can do I&#x2F;O&quot; capability rode in &lt;em&gt;inside&lt;&#x2F;em&gt; the &lt;code&gt;*Logger&lt;&#x2F;code&gt; value, invisibly. An effect-typed system would have rejected this until you either added the effect to &lt;code&gt;pureLooking&lt;&#x2F;code&gt;&#x27;s row or handled it locally.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;the-underlying-distinction&quot;&gt;The underlying distinction&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Effect row in the type&lt;&#x2F;strong&gt; → the compiler knows, statically and structurally, which effects every function may perform, and &lt;em&gt;forces&lt;&#x2F;em&gt; that knowledge to flow up the call graph.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Capability as a value&lt;&#x2F;strong&gt; (Zig&#x27;s choice) → the compiler only knows &quot;this function received an &lt;code&gt;Io&lt;&#x2F;code&gt;-shaped value.&quot; Whether it uses it, hides it, or passes it to ten other functions is invisible at the type level. Discipline is by convention, not by the checker.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Zig gives you the &lt;em&gt;runtime&lt;&#x2F;em&gt; benefit of swappable interpretations without the &lt;em&gt;static&lt;&#x2F;em&gt; benefit of guaranteed effect tracking.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;enter-scala-3-s-experimental-capture-checking-caprese&quot;&gt;Enter Scala 3&#x27;s experimental capture checking (Caprese)&lt;&#x2F;h2&gt;
&lt;p&gt;Scala 3 ships an experimental capture-checking feature, enabled with &lt;code&gt;import language.experimental.captureChecking&lt;&#x2F;code&gt;. The Scala documentation flags it explicitly as a research project, evolving quickly. The associated research effort is sometimes called Caprese.&lt;&#x2F;p&gt;
&lt;p&gt;Both Zig&#x27;s &lt;code&gt;Io&lt;&#x2F;code&gt; and Scala&#x27;s capture checking converge on the same philosophical move - &lt;em&gt;&quot;the ability to do X is a value, not an ambient power&quot;&lt;&#x2F;em&gt; - and both reject monadic I&#x2F;O &#x2F; function colouring. The split is whether the type system &lt;strong&gt;tracks&lt;&#x2F;strong&gt; where the capability flows.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;what-they-share&quot;&gt;What they share&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Capabilities-as-values.&lt;&#x2F;strong&gt; Scala&#x27;s &lt;code&gt;IO&lt;&#x2F;code&gt;, &lt;code&gt;FileSystem&lt;&#x2F;code&gt;, &lt;code&gt;Async&lt;&#x2F;code&gt; and Zig&#x27;s &lt;code&gt;Io&lt;&#x2F;code&gt; are values you receive as parameters and pass on. In the intended capability discipline I&#x2F;O authority is explicit rather than ambient; the checker tracks captured capabilities instead of requiring a monadic &lt;code&gt;IO[T]&lt;&#x2F;code&gt; wrapper.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Decoupled interpretation.&lt;&#x2F;strong&gt; Calling code is identical regardless of which interpreter is plugged in.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;No function colouring.&lt;&#x2F;strong&gt; A function that does I&#x2F;O looks like any other function - it just takes an extra parameter (Zig) or carries a capture annotation on its arrow (Scala).&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;what-capture-checking-adds-the-static-layer&quot;&gt;What capture checking adds: the static layer&lt;&#x2F;h3&gt;
&lt;p&gt;Capture checking encodes &quot;which capabilities did this value or closure capture?&quot; &lt;em&gt;into the type&lt;&#x2F;em&gt;. The current syntax puts capture annotations on the function arrow: &lt;code&gt;-&amp;gt;&lt;&#x2F;code&gt; is a pure arrow that captures nothing, &lt;code&gt;-&amp;gt;{c, d}&lt;&#x2F;code&gt; may use capabilities &lt;code&gt;c&lt;&#x2F;code&gt; and &lt;code&gt;d&lt;&#x2F;code&gt;, and &lt;code&gt;=&amp;gt;&lt;&#x2F;code&gt; is treated as the impure default that may capture arbitrary capabilities. Capability values themselves carry a &lt;code&gt;^&lt;&#x2F;code&gt; marker on their type.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;scala&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-declaration z-stable z-scala&quot;&gt;val&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; pure&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-class&quot;&gt; String&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-class&quot;&gt; Int&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  s &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;=&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; s.length                    &lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;&#x2F;&#x2F; pure arrow, captures nothing&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-declaration z-stable z-scala&quot;&gt;val&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; impure&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-class&quot;&gt; String&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;{fs}&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-class&quot;&gt; Long&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  path &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;=&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-bracket&quot;&gt; fs.size(path)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;            &#x2F;&#x2F; captures fs&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;class&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-class&quot;&gt; Logger&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-bracket z-variable&quot;&gt;(xfs:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-class&quot;&gt; FileSystem&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;^&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-bracket&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;    &#x2F;&#x2F; a class that retains a capability&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;  def&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; log&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-bracket z-variable&quot;&gt;(s:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-class&quot;&gt; String&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-bracket&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-class&quot;&gt; Unit&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-bracket&quot;&gt; xfs.append(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-scala z-string z-quoted z-double z-scala z-punctuation z-definition z-string z-end z-scala&quot;&gt;&amp;quot;log&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-bracket&quot;&gt;, s)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;&#x2F;&#x2F; inferred type carries the captured capability, e.g. Logger^{xfs}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;That gives Scala things Zig structurally cannot give you:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Capability-disciplined purity.&lt;&#x2F;strong&gt; A function value with an empty capture set is guaranteed not to capture any tracked capabilities. In a capability-disciplined subset where all I&#x2F;O flows through tracked capabilities, this gives you a strong purity guarantee - the docs put it conditionally: &lt;em&gt;if&lt;&#x2F;em&gt; capabilities are the only means to induce side effects, then capability polymorphism corresponds to effect polymorphism. On the JVM at large, ambient effects still exist; the guarantee is relative to the discipline.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Effect &#x2F; capability polymorphism.&lt;&#x2F;strong&gt; You can abstract over the capabilities a function may use, with explicit capture variables. A higher-order function can be parametric over &lt;em&gt;which&lt;&#x2F;em&gt; capabilities its callback needs, and the result type tells the caller exactly what was captured. Zig has no analogue; an &lt;code&gt;Io&lt;&#x2F;code&gt;-using callback just propagates the &lt;code&gt;Io&lt;&#x2F;code&gt; parameter manually.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Scope-bounded capabilities.&lt;&#x2F;strong&gt; The type system can guarantee a capability doesn&#x27;t escape the scope that granted it:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;scala&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-class&quot;&gt;Resource&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;.use { fs &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;=&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;  &#x2F;&#x2F; fs : FileSystem^ is local; cannot be returned, stored in&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;  &#x2F;&#x2F; a longer-lived ref, or captured by an escaping closure&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This makes structured resource handling &lt;em&gt;enforced&lt;&#x2F;em&gt;, not advisory. In Zig, once you hold an &lt;code&gt;Io&lt;&#x2F;code&gt;, you can stash it in any struct of any lifetime and the compiler is silent.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Capability retention is visible in the type.&lt;&#x2F;strong&gt; When a class holds a capability in a field, instances acquire a captured type (e.g.  &lt;code&gt;Logger^{xfs}&lt;&#x2F;code&gt;). Generic containers introduce nuance - capture information can &lt;em&gt;tunnel&lt;&#x2F;em&gt; through a container&#x27;s projections rather than always surfacing as a simple outer capture set - but the principle is that retention is tracked. The hidden-&lt;code&gt;Io&lt;&#x2F;code&gt; example from the earlier section is structurally invisible in Zig; its Scala analogue is not.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h3 id=&quot;two-way-comparison&quot;&gt;Two-way comparison&lt;&#x2F;h3&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;&lt;&#x2F;th&gt;&lt;th&gt;Zig &lt;code&gt;Io&lt;&#x2F;code&gt;&lt;&#x2F;th&gt;&lt;th&gt;Scala capture checking&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Capability is a value&lt;&#x2F;td&gt;&lt;td&gt;yes&lt;&#x2F;td&gt;&lt;td&gt;yes&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Avoids function colouring&lt;&#x2F;td&gt;&lt;td&gt;yes (with plumbing)&lt;&#x2F;td&gt;&lt;td&gt;yes&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Runtime-swappable interpreter&lt;&#x2F;td&gt;&lt;td&gt;yes (interface dispatch)&lt;&#x2F;td&gt;&lt;td&gt;yes (different instance)&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Pure functions distinguishable in type&lt;&#x2F;td&gt;&lt;td&gt;&lt;strong&gt;no&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;strong&gt;yes&lt;&#x2F;strong&gt; (under the discipline)&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Capability hidden in struct is tracked&lt;&#x2F;td&gt;&lt;td&gt;&lt;strong&gt;no&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;strong&gt;yes&lt;&#x2F;strong&gt; (with capture-tunneling nuance)&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Polymorphism over effects&lt;&#x2F;td&gt;&lt;td&gt;&lt;strong&gt;no&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;strong&gt;yes&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Compiler prevents capability escape&lt;&#x2F;td&gt;&lt;td&gt;&lt;strong&gt;no&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;strong&gt;yes&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Maturity&lt;&#x2F;td&gt;&lt;td&gt;new in 0.16&lt;&#x2F;td&gt;&lt;td&gt;experimental research&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Learning curve&lt;&#x2F;td&gt;&lt;td&gt;trivial&lt;&#x2F;td&gt;&lt;td&gt;significant&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;h3 id=&quot;how-to-think-about-it&quot;&gt;How to think about it&lt;&#x2F;h3&gt;
&lt;p&gt;Capture checking is &lt;strong&gt;what Zig&#x27;s &lt;code&gt;Io&lt;&#x2F;code&gt; would become if you bolted an effect-tracking type system on top of it.&lt;&#x2F;strong&gt; The runtime model is essentially the same - capabilities are plain values, dispatch is dynamic, there is no monad. Capture checking adds a &lt;em&gt;bookkeeping layer in the type checker&lt;&#x2F;em&gt; that records which capabilities each value transitively holds, and refuses programs where capabilities flow somewhere they should not.&lt;&#x2F;p&gt;
&lt;p&gt;The trade-off is honest:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Zig&lt;&#x2F;strong&gt; gets you most of the engineering benefit (swappable I&#x2F;O, no colouring, testability) for ~0% of the language complexity. You lose static guarantees: a function&#x27;s signature does not tell you what it can do.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Scala&lt;&#x2F;strong&gt; gets you the static guarantees at the cost of a non-trivial type-system feature that interacts with everything else (variance, inference, given&#x2F;using, etc.).&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;code&gt;Io&lt;&#x2F;code&gt; is the &lt;em&gt;runtime substrate&lt;&#x2F;em&gt; of a capability system without the &lt;em&gt;type-level discipline&lt;&#x2F;em&gt;. Capture checking is what you get when you keep the substrate and add the discipline back - without going all the way to algebraic effects with handlers.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;enter-kyo&quot;&gt;Enter Kyo&lt;&#x2F;h2&gt;
&lt;p&gt;Kyo sits in a third corner of the design space - it is the only one of the three whose public model is directly algebraic-effect-like: effects are pending in the return type, compose as a type-level set, and are discharged by handlers.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;what-kyo-actually-is&quot;&gt;What Kyo actually is&lt;&#x2F;h3&gt;
&lt;p&gt;Kyo encodes effects in the &lt;em&gt;return type&lt;&#x2F;em&gt; using a pending-effects operator &lt;code&gt;&amp;lt;&lt;&#x2F;code&gt;. Its current core effects include &lt;code&gt;Sync&lt;&#x2F;code&gt; (side effects), &lt;code&gt;Async&lt;&#x2F;code&gt;, &lt;code&gt;Scope&lt;&#x2F;code&gt; (resource lifecycle), and &lt;code&gt;Abort[E]&lt;&#x2F;code&gt; (typed failure). The README describes the design as based on algebraic effects and modular handlers, with pending effects represented as unordered type-level sets via intersection.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;scala&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;def&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; readConfig&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-bracket z-variable&quot;&gt;(path:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-class&quot;&gt; String&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-bracket&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-class&quot;&gt; String&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; &amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-bracket&quot;&gt; (&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-class&quot;&gt;Sync&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-class&quot;&gt; Abort&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-bracket&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-class&quot;&gt;ConfigError&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-bracket&quot;&gt;])&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; ...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;def&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; parse&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-bracket z-variable&quot;&gt;(s:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-class&quot;&gt; String&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-bracket&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-class&quot;&gt; Config&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; &amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-class&quot;&gt; Abort&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-bracket&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-class&quot;&gt;ConfigError&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-bracket&quot;&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;                  =&lt;&#x2F;span&gt;&lt;span&gt; ...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-declaration z-stable z-scala&quot;&gt;val&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; program&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-class&quot;&gt; Config&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; &amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-bracket&quot;&gt; (&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-class&quot;&gt;Sync&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-class&quot;&gt; Abort&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-bracket&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-class&quot;&gt;ConfigError&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-bracket&quot;&gt;])&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-meta z-bracket&quot;&gt;  readConfig(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-scala z-string z-quoted z-double z-scala z-punctuation z-definition z-string z-end z-scala&quot;&gt;&amp;quot;conf.json&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-bracket&quot;&gt;).map(parse)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;&#x2F;&#x2F; Handlers discharge effects, peeling them off the pending set:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-declaration z-stable z-scala&quot;&gt;val&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; safe&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-class&quot;&gt; Result&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-bracket&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-class&quot;&gt;ConfigError&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-class&quot;&gt;Config&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-bracket&quot;&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; &amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-class&quot;&gt; Sync&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-class&quot;&gt;  Abort&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-bracket&quot;&gt;.run(program)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;&#x2F;&#x2F; The remaining Sync is normally discharged at the application boundary&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;&#x2F;&#x2F; (e.g. by extending KyoApp), not by ad-hoc unsafe runs.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Three things to notice:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Effects appear in the type, not in the parameter list.&lt;&#x2F;strong&gt; For ordinary effects such as &lt;code&gt;Sync&lt;&#x2F;code&gt; or &lt;code&gt;Abort&lt;&#x2F;code&gt;, &lt;code&gt;readConfig&lt;&#x2F;code&gt; does not take an &lt;code&gt;io: IO&lt;&#x2F;code&gt; parameter; the requirement is encoded in the pending-effects type. (Kyo can still model environments and services, but those are themselves represented as effects rather than as manually threaded interface values.)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Effects compose by intersection&lt;&#x2F;strong&gt; in the type (&lt;code&gt;Sync &amp;amp; Abort[E]&lt;&#x2F;code&gt;), not by passing more parameters.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Handlers discharge effects.&lt;&#x2F;strong&gt; &lt;code&gt;Abort.run&lt;&#x2F;code&gt;, &lt;code&gt;Async.run&lt;&#x2F;code&gt;, and the &lt;code&gt;KyoApp&lt;&#x2F;code&gt; boundary that runs &lt;code&gt;Sync&lt;&#x2F;code&gt;, peel an effect off the pending set, transforming the type. This is the algebraic-effect handler shape - code is &lt;em&gt;interpretation-free&lt;&#x2F;em&gt; until a handler runs it.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Under the hood, Kyo is a single CPS-trampoline encoding (not transformer stacks), so it gets direct-style-ish performance with a uniform composition story. It is a &lt;em&gt;Scala library&lt;&#x2F;em&gt; encoding of algebraic effects, not a language feature like OCaml 5 or Koka - worth keeping in mind when comparing it to those.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;three-way-table&quot;&gt;Three-way table&lt;&#x2F;h3&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;&lt;&#x2F;th&gt;&lt;th&gt;Zig &lt;code&gt;Io&lt;&#x2F;code&gt;&lt;&#x2F;th&gt;&lt;th&gt;Scala capture checking&lt;&#x2F;th&gt;&lt;th&gt;Scala Kyo&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Where the effect lives&lt;&#x2F;td&gt;&lt;td&gt;parameter value&lt;&#x2F;td&gt;&lt;td&gt;capture set on the type&lt;&#x2F;td&gt;&lt;td&gt;pending set on the return type&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Effect appears in signature&lt;&#x2F;td&gt;&lt;td&gt;no&lt;&#x2F;td&gt;&lt;td&gt;yes (&lt;code&gt;-&amp;gt;{io,fs}&lt;&#x2F;code&gt;)&lt;&#x2F;td&gt;&lt;td&gt;yes (&lt;code&gt;&amp;lt; (Sync &amp;amp; Fs)&lt;&#x2F;code&gt;)&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Pure code distinguishable&lt;&#x2F;td&gt;&lt;td&gt;no&lt;&#x2F;td&gt;&lt;td&gt;yes (under the discipline)&lt;&#x2F;td&gt;&lt;td&gt;yes (&lt;code&gt;A&lt;&#x2F;code&gt; vs &lt;code&gt;A &amp;lt; S&lt;&#x2F;code&gt;)&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Effect &#x2F; capability polymorphism&lt;&#x2F;td&gt;&lt;td&gt;no&lt;&#x2F;td&gt;&lt;td&gt;yes&lt;&#x2F;td&gt;&lt;td&gt;yes (poly over &lt;code&gt;S&lt;&#x2F;code&gt;)&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Handler-based discharge&lt;&#x2F;td&gt;&lt;td&gt;&lt;strong&gt;no&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;strong&gt;no&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;strong&gt;yes&lt;&#x2F;strong&gt; (&lt;code&gt;Abort.run&lt;&#x2F;code&gt;, &lt;code&gt;Async.run&lt;&#x2F;code&gt;, &lt;code&gt;KyoApp&lt;&#x2F;code&gt;, …)&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;User-defined non-determinism, backtracking, custom scheduling&lt;&#x2F;td&gt;&lt;td&gt;no&lt;&#x2F;td&gt;&lt;td&gt;no&lt;&#x2F;td&gt;&lt;td&gt;&lt;strong&gt;yes&lt;&#x2F;strong&gt; (handler controls the continuation)&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Composition mechanism&lt;&#x2F;td&gt;&lt;td&gt;n&#x2F;a&lt;&#x2F;td&gt;&lt;td&gt;union of capture sets&lt;&#x2F;td&gt;&lt;td&gt;intersection of effect types&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Resource scoping enforced by checker&lt;&#x2F;td&gt;&lt;td&gt;no&lt;&#x2F;td&gt;&lt;td&gt;yes (escape-checked)&lt;&#x2F;td&gt;&lt;td&gt;partially, via &lt;code&gt;Scope&lt;&#x2F;code&gt; and disciplined handlers&#x2F;runtime boundaries&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Runtime model&lt;&#x2F;td&gt;&lt;td&gt;interface dispatch&lt;&#x2F;td&gt;&lt;td&gt;plain values&lt;&#x2F;td&gt;&lt;td&gt;CPS trampoline&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Avoids function colouring&lt;&#x2F;td&gt;&lt;td&gt;yes (with plumbing)&lt;&#x2F;td&gt;&lt;td&gt;yes&lt;&#x2F;td&gt;&lt;td&gt;yes-ish (the colour moves into the &lt;em&gt;type&lt;&#x2F;em&gt;)&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Learning curve&lt;&#x2F;td&gt;&lt;td&gt;trivial&lt;&#x2F;td&gt;&lt;td&gt;significant&lt;&#x2F;td&gt;&lt;td&gt;significant (different shape from capture checking)&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;h3 id=&quot;the-three-philosophies-in-one-line-each&quot;&gt;The three philosophies in one line each&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Zig &lt;code&gt;Io&lt;&#x2F;code&gt;&lt;&#x2F;strong&gt; - &lt;em&gt;&quot;the ability is a value; the type system stays out of it.&quot;&lt;&#x2F;em&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Capture checking&lt;&#x2F;strong&gt; - &lt;em&gt;&quot;the ability is a value; the type system tracks where the value flows.&quot;&lt;&#x2F;em&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Kyo&lt;&#x2F;strong&gt; - &lt;em&gt;&quot;the ability is a type; handlers interpret it at the boundary.&quot;&lt;&#x2F;em&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;where-kyo-gives-you-something-the-other-two-structurally-cannot&quot;&gt;Where Kyo gives you something the other two structurally cannot&lt;&#x2F;h3&gt;
&lt;p&gt;Both Zig and capture checking let you &lt;strong&gt;swap implementations&lt;&#x2F;strong&gt; - pass a different &lt;code&gt;Io&lt;&#x2F;code&gt;, pass a different &lt;code&gt;FileSystem&lt;&#x2F;code&gt;. That is enough for &quot;blocking vs. async vs. fake for tests.&quot; But neither lets the &lt;em&gt;handler&lt;&#x2F;em&gt; control the &lt;em&gt;control flow&lt;&#x2F;em&gt; of the effectful code. Kyo does, because handlers receive a continuation:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Non-determinism&lt;&#x2F;strong&gt;: a &lt;code&gt;Choice&lt;&#x2F;code&gt; handler that runs the rest of the computation once per branch and collects results. You cannot express this by swapping a vtable - it requires capturing what comes after the effect.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Backtracking &#x2F; search&lt;&#x2F;strong&gt;: handler peeks at intermediate results and decides whether to resume, restart, or abandon.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Custom schedulers&lt;&#x2F;strong&gt;: an &lt;code&gt;Async&lt;&#x2F;code&gt; handler that decides ordering, fairness, cancellation policy entirely in user code.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Effect translation&lt;&#x2F;strong&gt;: a handler that converts &lt;code&gt;Abort[E1]&lt;&#x2F;code&gt; into &lt;code&gt;Abort[E2]&lt;&#x2F;code&gt;, or interprets &lt;code&gt;Sync&lt;&#x2F;code&gt; as &lt;code&gt;Trace&lt;&#x2F;code&gt; (logging every operation without performing it) for testing.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Capture checking cannot do this directly because capabilities are just &lt;em&gt;values&lt;&#x2F;em&gt; - they do not reify the continuation of the caller. Zig cannot do this because there is no notion of a handler at all; you only pick the implementation, not the control structure.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;where-capture-checking-gives-you-something-kyo-doesn-t&quot;&gt;Where capture checking gives you something Kyo doesn&#x27;t&lt;&#x2F;h3&gt;
&lt;p&gt;Capture checking still wins on one axis: &lt;strong&gt;enforced lexical scoping of capabilities.&lt;&#x2F;strong&gt; It can prove a &lt;code&gt;FileSystem&lt;&#x2F;code&gt; doesn&#x27;t escape &lt;code&gt;Resource.use { fs =&amp;gt; ... }&lt;&#x2F;code&gt; because the type system refuses to let &lt;code&gt;fs&lt;&#x2F;code&gt; flow into a longer-lived type. Kyo can model this with &lt;code&gt;Scope&lt;&#x2F;code&gt;, but the guarantee comes from the handler&#x2F;runtime discipline, not from a structural impossibility - a poorly written handler could leak a resource. (In practice Kyo&#x27;s standard handlers are correct, but the &lt;em&gt;type system&lt;&#x2F;em&gt; is not what is stopping you.)&lt;&#x2F;p&gt;
&lt;p&gt;Conversely, capture checking can&#x27;t express &quot;run this block 17 times under a different interpretation each time,&quot; which Kyo gets for free.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;synthesis-a-spectrum-not-a-hierarchy&quot;&gt;Synthesis: a spectrum, not a hierarchy&lt;&#x2F;h2&gt;
&lt;p&gt;Mapping back to the original question — &lt;em&gt;is Zig&#x27;s &lt;code&gt;Io&lt;&#x2F;code&gt; an effect?&lt;&#x2F;em&gt; — the honest answer is that &quot;effect&quot; is a continuum, and these three systems sit at distinct points along it:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Zig Io  ──►  capture checking  ──►  Kyo  ──►  Koka &#x2F; Eff   and   OCaml 5&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;(none)        (capture sets)       (lib AE)   (language AE; Koka&#x2F;Eff are&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                                               effect-typed, OCaml 5 has&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                                               handlers without static&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                                               effect safety and only&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                                               one-shot continuations)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;   │              │                    │            │&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;   │              │                    │            └─ Effects + handlers as&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;   │              │                    │               a first-class language&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;   │              │                    │               feature.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;   │              │                    │&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;   │              │                    └─ Effects in types, handlers in user&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;   │              │                       code, implemented as a library on a&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;   │              │                       CPS runtime.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;   │              │&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;   │              └─ Capabilities as values, with type-system tracking of&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;   │                 where capabilities flow. No handlers — interpretation&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;   │                 is chosen by which capability value you pass.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;   │&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;   └─ Capabilities as values. No type tracking, no handlers.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      Engineering benefits of effects without the formal machinery.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Each step rightward buys more guarantees and more expressive control flow at the cost of more language or library complexity, more annotations, and (often) more runtime indirection.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;a-final-philosophical-reading&quot;&gt;A final philosophical reading&lt;&#x2F;h3&gt;
&lt;p&gt;There is a useful way to see the progression:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Zig &lt;code&gt;Io&lt;&#x2F;code&gt;&lt;&#x2F;strong&gt; treats effects as a &lt;em&gt;runtime engineering problem&lt;&#x2F;em&gt;: &quot;we need the ability to swap implementations and avoid colouring functions.&quot; It solves exactly that and refuses to pay for anything more.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Capture checking&lt;&#x2F;strong&gt; treats effects as a &lt;em&gt;flow-of-authority problem&lt;&#x2F;em&gt;: &quot;if a piece of code holds the ability to do X, the type system should know, and should be able to prevent that ability from leaking past where it was granted.&quot; It keeps the value-level model and adds a static accounting layer on top.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Kyo&lt;&#x2F;strong&gt; (and Koka, OCaml 5) treats effects as a &lt;em&gt;control-flow problem&lt;&#x2F;em&gt;: &quot;operations are syntactic markers whose meaning is supplied by the surrounding handler, including how - or whether - to resume.&quot; Effects stop being about &lt;em&gt;who has permission&lt;&#x2F;em&gt; and become about &lt;em&gt;who decides what happens next&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Zig&#x27;s &lt;code&gt;Io&lt;&#x2F;code&gt; is, by this lens, the minimal viable point in the design space: enough effect-shape to fix function colouring and enable testing, no more. Whether that is &quot;really&quot; an effect depends on which sense of &quot;effect&quot; from §0 you have in mind - and on whether you are asking a language theorist or a working systems programmer. The theorist will say no in sense (3); the systems programmer will note that they got most of the benefit they cared about, with none of the syntactic or runtime cost - and that, for a language whose target audience is largely the second group, is probably the correct trade.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;appendix-status-as-of-writing&quot;&gt;Appendix: status as of writing&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Zig &lt;code&gt;std.Io&lt;&#x2F;code&gt;&lt;&#x2F;strong&gt; is new in 0.16.0 (April 2026). The threaded backend is feature-complete; evented, &lt;code&gt;io_uring&lt;&#x2F;code&gt;, &lt;code&gt;kqueue&lt;&#x2F;code&gt;, and Grand-Central-Dispatch backends range from work-in-progress to proof-of-concept. &lt;code&gt;std.testing.io&lt;&#x2F;code&gt; is the recommended fake for tests.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Scala 3 capture checking&lt;&#x2F;strong&gt; is enabled via &lt;code&gt;import language.experimental.captureChecking&lt;&#x2F;code&gt; and is documented as a research project that is evolving quickly. Function-arrow syntax (&lt;code&gt;-&amp;gt;&lt;&#x2F;code&gt;, &lt;code&gt;-&amp;gt;{c}&lt;&#x2F;code&gt;, &lt;code&gt;=&amp;gt;&lt;&#x2F;code&gt;) and capture markers (&lt;code&gt;T^&lt;&#x2F;code&gt;, &lt;code&gt;T^{c}&lt;&#x2F;code&gt;) reflect the current shape; details may shift.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Kyo&#x27;s&lt;&#x2F;strong&gt; core effects today are &lt;code&gt;Sync&lt;&#x2F;code&gt;, &lt;code&gt;Async&lt;&#x2F;code&gt;, &lt;code&gt;Scope&lt;&#x2F;code&gt;, and &lt;code&gt;Abort&lt;&#x2F;code&gt; - not the older &lt;code&gt;IO&lt;&#x2F;code&gt; &#x2F; &lt;code&gt;Resource&lt;&#x2F;code&gt; terminology. The README treats &lt;code&gt;KyoApp&lt;&#x2F;code&gt; as the standard application boundary that discharges &lt;code&gt;Sync&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</description>
      </item>
      <item>
          <title>Evaluating PBT Frameworks: How Proptest and Hegel Differ in Algebraic Expressivity</title>
          <pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://debasishg.github.io/blog/proptest-hegel/</link>
          <guid>https://debasishg.github.io/blog/proptest-hegel/</guid>
          <description xml:base="https://debasishg.github.io/blog/proptest-hegel/">&lt;h1 id=&quot;evaluating-pbt-frameworks-how-proptest-and-hegel-differ-in-algebraic-expressivity&quot;&gt;Evaluating PBT Frameworks: How Proptest and Hegel Differ in Algebraic Expressivity&lt;&#x2F;h1&gt;
&lt;h2 id=&quot;context&quot;&gt;Context&lt;&#x2F;h2&gt;
&lt;p&gt;I have recently been working on &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;debasishg&#x2F;porcupine-rust&quot;&gt;&lt;code&gt;porcupine-rust&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; is a Rust port of &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;anishathalye&#x2F;porcupine&quot;&gt;Porcupine&lt;&#x2F;a&gt;, a linearizability checker for concurrent and distributed systems, with APIs over timestamped &lt;code&gt;Operation&lt;&#x2F;code&gt; histories and raw &lt;code&gt;Event&lt;&#x2F;code&gt; histories, optional timeout-bounded checking, P-compositional partitioning, and support for nondeterministic step semantics through &lt;code&gt;NondeterministicModel&lt;&#x2F;code&gt; and &lt;code&gt;PowerSetModel&lt;&#x2F;code&gt;. In the same codebase, the runtime invariant layer explicitly ties implementation checks back to &lt;code&gt;INV-*&lt;&#x2F;code&gt; identifiers in &lt;code&gt;docs&#x2F;spec.md&lt;&#x2F;code&gt;, including well-formed history, minimal-call frontier, partition independence, and cache soundness. That mix of algebraic laws, history-shape constraints, and incremental trace reasoning is exactly the kind of workload that exposes the real design trade-offs between testing libraries.&lt;&#x2F;p&gt;
&lt;p&gt;The project’s testing setup from the inside reveals two property-based testing implementations - &lt;code&gt;tests&#x2F;property_tests.rs&lt;&#x2F;code&gt; (using &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.rs&#x2F;proptest&#x2F;latest&#x2F;proptest&#x2F;&quot;&gt;proptest&lt;&#x2F;a&gt;) and &lt;code&gt;tests&#x2F;hegel_properties.rs&lt;&#x2F;code&gt; (using &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.rs&#x2F;hegeltest&#x2F;latest&#x2F;hegel&#x2F;&quot;&gt;hegel&lt;&#x2F;a&gt;). They are deliberately near-mirror suites over the same invariants, with shared builders under &lt;code&gt;tests&#x2F;common&#x2F;&lt;&#x2F;code&gt;, while also documenting the handful of places where the Hegel suite genuinely goes beyond the proptest one. That makes this repository a particularly good benchmark for “same domain, same models, same assertions, different property-testing architecture”.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;why-porcupine-rust-makes-this-comparison-unusually-revealing&quot;&gt;Why porcupine-rust makes this comparison unusually revealing&lt;&#x2F;h2&gt;
&lt;p&gt;The repository’s core problem is &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;arxiv.org&#x2F;abs&#x2F;1504.00204&quot;&gt;linearizability&lt;&#x2F;a&gt;: given a partially ordered history of calls and returns, the checker has to decide whether those operations can be linearized into some sequential history consistent with the model. The README makes clear that the implementation is, in essence, a search engine over histories, with DFS&#x2F;backtracking, bitset-based state tracking, partition-aware checking, and optional nondeterministic model support. Those characteristics matter for testing because failures are rarely “one bad scalar”, they are usually tangled histories with subtle timing relationships, partition boundaries, or branching semantics.&lt;&#x2F;p&gt;
&lt;p&gt;That is also why the invariant layer matters so much. In &lt;code&gt;src&#x2F;invariants.rs&lt;&#x2F;code&gt;, the code documents that every macro or function corresponds to an &lt;code&gt;INV-*&lt;&#x2F;code&gt; identifier in &lt;code&gt;docs&#x2F;spec.md&lt;&#x2F;code&gt;, and the partition-related checks nail down strong contracts: partitions must be disjoint, complete, in-bounds, and for event histories, keep each &lt;code&gt;(Call, Return)&lt;&#x2F;code&gt; pair together. Those are higher-order obligations over histories, not merely per-field sanity checks. A property-testing library used here therefore has to be good at constrained structured generation, shrinking of closely-related values, and, in the stateful case, construction of histories that remain meaningful as they are simplified.&lt;&#x2F;p&gt;
&lt;p&gt;There is a subtle but crucial caveat though: neither test suite is executing live concurrent Rust code with threads or async runtimes. Both suites are checking linearizability properties over pre-built histories. So the argument is not “which library is better at race testing by itself”; it is “which library better expresses the generation and shrinking of concurrent-looking histories and prefixes”. That framing is important, because it is exactly where Hegel’s state-machine API starts to matter and where proptest’s batch-generated histories remain perfectly adequate for a large class of checks.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;proptest-and-hegel-in-one-paragraph-each&quot;&gt;Proptest and Hegel in one paragraph each&lt;&#x2F;h2&gt;
&lt;p&gt;Proptest is the established Rust-native member of the QuickCheck family. Its central abstraction is the &lt;code&gt;Strategy&lt;&#x2F;code&gt;, which defines both how values are generated and how they are shrunk. The Proptest book describes generation and shrinking as the two defining duties of a strategy, and the project describes the crate as feature-complete enough to be in long-term passive maintenance. In practical Rust terms, Proptest’s worldview is: build a typed generator graph, compose it with strategy combinators such as &lt;code&gt;prop_map&lt;&#x2F;code&gt;, &lt;code&gt;prop_flat_map&lt;&#x2F;code&gt;, &lt;code&gt;prop_filter&lt;&#x2F;code&gt;, and &lt;code&gt;prop_compose!&lt;&#x2F;code&gt;, and then feed the resulting values into a test body.&lt;&#x2F;p&gt;
&lt;p&gt;Hegel, by contrast, is a Rust property-testing library built on Hypothesis through the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;hegel.dev&quot;&gt;Hegel protocol&lt;&#x2F;a&gt;. Its centre of gravity is the live &lt;code&gt;TestCase&lt;&#x2F;code&gt;: you draw values imperatively with &lt;code&gt;tc.draw(...)&lt;&#x2F;code&gt;, reject inputs with &lt;code&gt;tc.assume(...)&lt;&#x2F;code&gt;, attach debugging information with &lt;code&gt;tc.note(...)&lt;&#x2F;code&gt;, and build dependent generators with &lt;code&gt;#[hegel::composite]&lt;&#x2F;code&gt;. The official documentation also makes stateful testing a first-class module, with &lt;code&gt;#[hegel::state_machine]&lt;&#x2F;code&gt;, &lt;code&gt;#[rule]&lt;&#x2F;code&gt;, &lt;code&gt;#[invariant]&lt;&#x2F;code&gt;, &lt;code&gt;stateful::run&lt;&#x2F;code&gt;, and &lt;code&gt;Variables&amp;lt;T&amp;gt;&lt;&#x2F;code&gt; for pools of previously generated values.&lt;&#x2F;p&gt;
&lt;p&gt;A concise way to describe the difference is this: Proptest localises randomness in strategy values, while Hegel keeps randomness live inside the test execution itself. That is not a matter of syntax alone. It changes whether generation is a prelude to the test or part of the test algorithm. In a repository like &lt;code&gt;porcupine-rust&lt;&#x2F;code&gt;, that architectural distinction ends up being more important than slogans like “stateless versus stateful”.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;where-both-libraries-are-equally-strong&quot;&gt;Where both libraries are equally strong&lt;&#x2F;h2&gt;
&lt;p&gt;The strongest point in favour of keeping both libraries is that a large part of the repository does not actually force a choice. The project&#x27;s strategy for enforcing invariants across spec and implementation suggests that for every &lt;code&gt;INV-*&lt;&#x2F;code&gt; invariant in &lt;code&gt;docs&#x2F;spec.md&lt;&#x2F;code&gt;, the two suites contain near-mirror tests using the same shared builders, and it names several such pairs explicitly: &lt;code&gt;prop_time_shift_invariance&lt;&#x2F;code&gt; versus &lt;code&gt;hegel_time_shift_invariance&lt;&#x2F;code&gt;, &lt;code&gt;prop_slice_order_invariance&lt;&#x2F;code&gt; versus &lt;code&gt;hegel_slice_order_invariance&lt;&#x2F;code&gt;, &lt;code&gt;prop_concurrent_write_overlap_read_matches_membership&lt;&#x2F;code&gt; versus its Hegel counterpart, &lt;code&gt;prop_powerset_eq_hashed_powerset&lt;&#x2F;code&gt; versus &lt;code&gt;hegel_powerset_eq_hashed_powerset&lt;&#x2F;code&gt;, and cache-soundness checks on both sides. In other words, for a large class of finished-history properties, the repository itself already demonstrates that the two frameworks are expressively interchangeable.&lt;&#x2F;p&gt;
&lt;p&gt;The best concrete example is &lt;strong&gt;dependent generation&lt;&#x2F;strong&gt;. Let us walk through &lt;code&gt;arb_overlap_write_read&lt;&#x2F;code&gt; in the proptest suite and its Hegel mirror in &lt;code&gt;tests&#x2F;hegel_properties.rs&lt;&#x2F;code&gt;. In proptest, the pattern is the familiar multi-stage &lt;code&gt;prop_compose!&lt;&#x2F;code&gt; form: first draw &lt;code&gt;write_dur&lt;&#x2F;code&gt;, then use &lt;code&gt;Just(write_dur)&lt;&#x2F;code&gt; to make that value available while drawing &lt;code&gt;read_call&lt;&#x2F;code&gt; from &lt;code&gt;0..write_dur&lt;&#x2F;code&gt;. That two-stage form is essentially sugar for &lt;code&gt;prop_flat_map&lt;&#x2F;code&gt;: the first stage produces a value, and the second stage builds a new strategy parameterized by it. Generation is still a function of strategies, evaluated before the test body runs. In Hegel, the same constraint is written directly with sequential draws: first &lt;code&gt;write_dur&lt;&#x2F;code&gt;, then &lt;code&gt;read_call&lt;&#x2F;code&gt; with its upper bound computed from that earlier draw. Both are principled encodings of a dependent generator - neither is resorting to rejection sampling.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;&lt;strong&gt;Rejection Sampling:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;a-naive-way-to-enforce-read-call-write-dur-would-be-to-draw-both-freely-and-then-throw-away-cases-that-violate-the-bound-rejection-sampling-which-wastes-work-and-confuses-shrinking-both-libraries-avoid-that-by-making-the-dependency-structural-the-difference-is-purely-ergonomic-proptest-expresses-it-through-strategy-combinators-hegel-through-ordinary-control-flow&quot;&gt;A naive way to enforce &lt;code&gt;read_call &amp;lt; write_dur&lt;&#x2F;code&gt; would be to draw both freely and then throw away cases that violate the bound (rejection sampling), which wastes work and confuses shrinking. Both libraries avoid that by making the dependency structural. The difference is purely ergonomic: proptest expresses it through strategy combinators, Hegel through ordinary control flow.&lt;&#x2F;h2&gt;
&lt;p&gt;A distilled version of that contrast looks like this:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;&#x2F;&#x2F; proptest style: dependency staged through the strategy.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;prop_compose!&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    fn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; overlap_history&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;()&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-variable&quot;&gt;        (write_dur&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; in&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 5&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;u64&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;..&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;30&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-variable&quot;&gt;, write_value&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; in&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;50&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;i64&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;..&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;50&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-variable&quot;&gt;        (write_dur&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; in&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; Just&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-variable&quot;&gt;(write_dur),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;         write_value&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; in&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; Just&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-variable&quot;&gt;(write_value),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;         read_call&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; in&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;u64&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;..&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt;write_dur)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; History&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;        &#x2F;&#x2F; build overlapping write&#x2F;read history&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;&#x2F;&#x2F; Hegel style: dependency expressed inline at the draw site.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;#[&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-attribute z-rust&quot;&gt;hegel&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-attribute z-rust&quot;&gt;composite&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;fn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; overlap_history&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-variable&quot;&gt;(tc&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; TestCase&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; History&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;    let&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; write_dur&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; tc&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;draw&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-namespace&quot;&gt;gs&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;integers&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;u64&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;gt;()&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;min_value&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;5&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;max_value&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;30&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;));&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;    let&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; write_value&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; tc&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;draw&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-namespace&quot;&gt;gs&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;integers&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;i64&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;gt;()&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;min_value&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;-&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;50&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;max_value&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;50&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;));&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;    let&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; read_call&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; tc&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;draw&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-namespace&quot;&gt;gs&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;integers&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;u64&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;gt;()&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;min_value&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;0&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;max_value&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-variable&quot;&gt;(write_dur&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;));&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;    &#x2F;&#x2F; build overlapping write&#x2F;read history&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Those snippets differ in ergonomics, not in logical power for this category of problem. both cleanly encode &lt;code&gt;read_call ∈ [0, write_dur)&lt;&#x2F;code&gt; without rejection sampling - but proptest forces the dependency into the shape of the strategy (two parameter lists, Just re-binding), while Hegel lets it look like ordinary imperative code.&lt;&#x2F;p&gt;
&lt;p&gt;The proptest book explicitly documents &lt;code&gt;prop_compose!&lt;&#x2F;code&gt; and &lt;code&gt;prop_flat_map&lt;&#x2F;code&gt; as the mechanism for value-dependent strategies, and the Hegel docs explicitly document feeding the result of one &lt;code&gt;draw&lt;&#x2F;code&gt; into later &lt;code&gt;draw&lt;&#x2F;code&gt; calls in &lt;code&gt;#[hegel::composite]&lt;&#x2F;code&gt; generators.&lt;&#x2F;p&gt;
&lt;p&gt;This equivalence matters because it prevents an overstatement that would otherwise be tempting: Hegel is not “more expressive” in the repository’s dominant workload of stateless or value-level history generation. For overlap windows, shaped reads, algebraic invariants, powerset&#x2F;hash equivalences, and deterministic cache checks, proptest’s strategy algebra and Hegel’s imperative draws are simply two different surfaces over the same basic generator logic. It would be right to say that, in this category, nothing in the repository is inherently beyond one framework or the other.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;where-hegel-is-materially-stronger-in-this-repository&quot;&gt;Where Hegel is materially stronger in this repository&lt;&#x2F;h2&gt;
&lt;p&gt;The expressive gap opens only when the history is not generated all at once, but grown step by step while the property is being checked. The test suite identifies three Hegel-only tests in &lt;code&gt;porcupine-rust&lt;&#x2F;code&gt;: &lt;code&gt;IncrementalKv&lt;&#x2F;code&gt;, &lt;code&gt;ConcurrentWritesChain&lt;&#x2F;code&gt;, and &lt;code&gt;IncrementalRegister&lt;&#x2F;code&gt;. In &lt;code&gt;IncrementalKv&lt;&#x2F;code&gt;, random writer and reader rules are selected repeatedly and the checker is asserted to return &lt;code&gt;Ok&lt;&#x2F;code&gt; on every prefix of the accumulated history. In &lt;code&gt;ConcurrentWritesChain&lt;&#x2F;code&gt;, the next operation’s legal call-time range is computed from state accumulated by previous rule firings. In &lt;code&gt;IncrementalRegister&lt;&#x2F;code&gt;, a newly generated read derives its expected output from a backward scan over prior history before the operation is appended. Those three tests are not just “stateful”; they interleave random generation, model-state inspection, mutation, and assertion in one loop.&lt;&#x2F;p&gt;
&lt;p&gt;Hegel’s stateful API is built exactly for that shape. The official stateful docs say that &lt;code&gt;#[rule]&lt;&#x2F;code&gt; methods mutate &lt;code&gt;&amp;amp;mut self&lt;&#x2F;code&gt;, &lt;code&gt;#[invariant]&lt;&#x2F;code&gt; methods are checked after each successful rule application, and &lt;code&gt;stateful::run&lt;&#x2F;code&gt; repeatedly applies random rules. The &lt;code&gt;StateMachine&lt;&#x2F;code&gt; trait itself is defined in terms of &lt;code&gt;rules()&lt;&#x2F;code&gt; and &lt;code&gt;invariants()&lt;&#x2F;code&gt;, and &lt;code&gt;Variables&amp;lt;T&amp;gt;&lt;&#x2F;code&gt; adds a symbolic pool of previously generated values when a state machine needs to carry handles or references forward across rule applications. In other words, the official abstraction and the repository’s most sophisticated tests line up almost perfectly.&lt;&#x2F;p&gt;
&lt;p&gt;A representative Hegel pattern for this repository looks like the following:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;#[&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-attribute z-rust&quot;&gt;hegel&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-attribute z-rust&quot;&gt;state_machine&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;impl&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; IncrementalKv&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;    #[&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-attribute z-rust&quot;&gt;rule&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    fn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; write_random_key&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-storage&quot;&gt;&amp;amp;mut&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language z-self&quot;&gt; self&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-variable&quot;&gt;, tc&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; TestCase&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;        &#x2F;&#x2F; draw key&#x2F;value&#x2F;timestamps, append operation&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;    #[&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-attribute z-rust&quot;&gt;rule&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    fn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; read_random_key&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-storage&quot;&gt;&amp;amp;mut&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language z-self&quot;&gt; self&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-variable&quot;&gt;, tc&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; TestCase&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;        &#x2F;&#x2F; draw key&#x2F;timestamps, compute expected read from current history, append&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;    #[&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-attribute z-rust&quot;&gt;invariant&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    fn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; prefixes_are_linearizable&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language z-self&quot;&gt;self&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-variable&quot;&gt;, _&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; TestCase&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;        assert_eq!&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;check_operations&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;KvModel&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language z-self&quot;&gt;self&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function&quot;&gt;history&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; None&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;),&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; CheckResult&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;Ok&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This is not just pleasantly ergonomic. It preserves the semantic unity between “how the next move is drawn”, “how state evolves”, and “what must hold now”. That unity is exactly what makes Hegel support &lt;em&gt;stateful, rule-scheduled, prefix-asserting traces with mid-test adaptive draws&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Proptest can approximate parts of that story, but it does so by moving complexity into the strategy layer. The proptest state-machine chapter requires a &lt;code&gt;ReferenceStateMachine&lt;&#x2F;code&gt; with &lt;code&gt;init_state&lt;&#x2F;code&gt;, &lt;code&gt;transitions(state)&lt;&#x2F;code&gt;, and &lt;code&gt;apply&lt;&#x2F;code&gt;, plus a &lt;code&gt;StateMachineTest&lt;&#x2F;code&gt; with separate &lt;code&gt;init_test&lt;&#x2F;code&gt;, &lt;code&gt;apply&lt;&#x2F;code&gt;, and &lt;code&gt;check_invariants&lt;&#x2F;code&gt;. The same chapter explains that the sequential strategy ultimately generates a &lt;code&gt;Vec&amp;lt;Transition&amp;gt;&lt;&#x2F;code&gt; that is then interpreted against the SUT. That is a perfectly respectable design, and it has real strengths, but it is a different shape from the Hegel rule body that can keep calling &lt;code&gt;tc.draw(...)&lt;&#x2F;code&gt; after inspecting current machine state.&lt;&#x2F;p&gt;
&lt;p&gt;The subtle gain Hegel gets here is not merely “stateful testing exists”. Proptest also has state-machine testing. The gain is that Hegel allows generation to remain imperative and state-sensitive &lt;em&gt;inside&lt;&#x2F;em&gt; the rule. The &lt;code&gt;ConcurrentWritesChain&lt;&#x2F;code&gt; example in the tests is the clearest illustration: the next call time is drawn from bounds computed from &lt;code&gt;self.last_call&lt;&#x2F;code&gt; and &lt;code&gt;self.last_return&lt;&#x2F;code&gt;, and that choice sits in the same body as the subsequent assertion over the whole history. In proptest-state-machine, the same logic would have to be split across &lt;code&gt;transitions(state)&lt;&#x2F;code&gt; for generation, &lt;code&gt;apply&lt;&#x2F;code&gt; for mutation and local postconditions, and &lt;code&gt;check_invariants&lt;&#x2F;code&gt; for global assertions. That is doable; it is just materially more fragmented.&lt;&#x2F;p&gt;
&lt;p&gt;There is also a shrinker story behind this. The incremental, prefix-sensitive failures tend to benefit from Hypothesis-style shrinking because the counterexample is simplified at the same granularity as rule firings rather than only as a final batch-produced move list. I would phrase that carefully: not “Hegel always shrinks better”, but “for the failure shape produced by incrementally constructed histories, Hegel’s model fits the bug more closely”. In a linearizability checker, that can be the difference between a five-step witness history and a forty-operation hairball.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;where-proptest-is-still-the-better-tool&quot;&gt;Where Proptest is still the better tool&lt;&#x2F;h2&gt;
&lt;p&gt;The first proptest advantage is operational, not theoretical: speed. A benchmark done reports a project-specific measurement at 100 cases per test of roughly &lt;code&gt;0.05 s&lt;&#x2F;code&gt; for &lt;code&gt;cargo test --test property_tests&lt;&#x2F;code&gt; versus roughly &lt;code&gt;2.2 s&lt;&#x2F;code&gt; for &lt;code&gt;cargo test --test hegel_properties&lt;&#x2F;code&gt;. That gap is entirely plausible given the official Hegel installation model: &lt;code&gt;hegeltest&lt;&#x2F;code&gt; uses &lt;code&gt;uv&lt;&#x2F;code&gt;, may install or download its server component, and Hegel’s own maintainers explicitly say that the current Python dependency is the main performance limiter. If you care about fast inner-loop feedback while editing the checker, proptest is the obvious default.&lt;&#x2F;p&gt;
&lt;p&gt;The second advantage is ecosystem maturity. Proptest describes itself as close to feature-complete and largely in passive maintenance, which is usually exactly what infrastructure code wants from a test dependency: low surprise, stable idioms, and broad community familiarity. Hegel’s crate documentation, by contrast, carries an explicit beta disclaimer and reserves the right to make breaking changes if that improves the library. That is not a criticism of Hegel’s design; it is a concrete engineering cost. For CI-critical test suites in production Rust repositories, this stability differential matters.&lt;&#x2F;p&gt;
&lt;p&gt;The third advantage is that proptest’s “generator first” model is often the right abstraction when the generated object is the thing you want to persist, replay, or compose. Its failure-persistence mechanism stores failing cases in &lt;code&gt;proptest-regressions&lt;&#x2F;code&gt; so later runs replay them before searching for fresh failures, and its state-machine API is explicit about the split between the abstract reference state and the SUT. This split points to a genuine strength when there is a real effectful system under test—say a database, a service process, or an external component—because it forces the model&#x2F;implementation distinction into the type shape of the harness.&lt;&#x2F;p&gt;
&lt;p&gt;The final Proptest advantage is that its combinator algebra rewards careful modelling. The official docs show that &lt;code&gt;prop_map&lt;&#x2F;code&gt; preserves the relationship to the original strategy so shrinking still happens in terms of the underlying source values, and &lt;code&gt;prop_flat_map&lt;&#x2F;code&gt; allows a later strategy to depend on an earlier drawn value while keeping invariants such as “the index stays within the vector”. In other words, Proptest is not weak at dependency management; it just insists that you make those dependencies explicit in the strategy graph. For teams that value typed, reusable, independently testable generators, that discipline is often a feature rather than a burden.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;which-design-patterns-belong-to-which-library&quot;&gt;Which design patterns belong to which library&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Choose Hegel when the trace is the test. If your property grows a history incrementally, checks every prefix, and must let later draws depend on already-mutated model state, Hegel is the more natural fit in Rust today. &lt;code&gt;porcupine-rust&lt;&#x2F;code&gt;’s &lt;code&gt;IncrementalKv&lt;&#x2F;code&gt;, &lt;code&gt;ConcurrentWritesChain&lt;&#x2F;code&gt;, and &lt;code&gt;IncrementalRegister&lt;&#x2F;code&gt; are exemplary here: the next operation is not merely sampleable from a static strategy; it is computed in dialogue with the current history. That design pattern maps directly onto &lt;code&gt;#[hegel::state_machine]&lt;&#x2F;code&gt;, &lt;code&gt;#[rule]&lt;&#x2F;code&gt;, &lt;code&gt;#[invariant]&lt;&#x2F;code&gt;, &lt;code&gt;tc.draw&lt;&#x2F;code&gt;, and &lt;code&gt;tc.assume&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Choose Hegel as well when you want imperative generator code that still shrinks coherently. The official &lt;code&gt;#[hegel::composite]&lt;&#x2F;code&gt; docs explicitly bless feeding one draw into subsequent draws, and the this is exactly how the Hegel versions of the porcupine history generators are expressed. In Rust terms, Hegel feels most at home when your generator wants to read like straight-line business logic with branches, helper calls, local calculations, and late assumptions.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Choose proptest when the input object is a value, not an interaction. That includes algebraic laws over finished histories, finished operation vectors, transformations over timestamps, partitioning rules, and cross-checks over pure model functions. It also includes cases where you want generators to be exported as reusable functions, composed through &lt;code&gt;prop_map&lt;&#x2F;code&gt;, &lt;code&gt;prop_flat_map&lt;&#x2F;code&gt;, &lt;code&gt;prop_oneof!&lt;&#x2F;code&gt;, and &lt;code&gt;prop_compose!&lt;&#x2F;code&gt;, and reused across many tests. That pattern is heavily represented in &lt;code&gt;porcupine-rust&lt;&#x2F;code&gt;’s mirrored value-level properties, and the repository’s own comparison note says the two frameworks are effectively interchangeable there.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Choose proptest when execution economics or harness architecture matter more than the last ounce of stateful elegance. If you want the fastest edit-run loop, a pure-Rust dependency with no sidecar, stored regression seeds, and a state-machine API that explicitly separates model from SUT, Proptest is the better day-to-day engineering choice. That is why it&#x27;s recommended for inner-loop CI work even while crediting Hegel for stronger stateful coverage.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The most useful general rule I can extract from &lt;code&gt;porcupine-rust&lt;&#x2F;code&gt; is this: &lt;strong&gt;proptest is strongest when generation is a specification artefact; Hegel is strongest when generation is part of the execution semantics of the property itself.&lt;&#x2F;strong&gt; That is why both libraries look equally capable on overlap-window generators, yet Hegel pulls ahead once the history is allowed to evolve under a live scheduler with per-prefix assertions. It is also why proptest remains the saner choice for fast, stable, repository-wide coverage over law-like properties.&lt;&#x2F;p&gt;
&lt;p&gt;The right conclusion, then, is not that one library wins. The right conclusion is that &lt;code&gt;porcupine-rust&lt;&#x2F;code&gt; exposes two different testing geometries. For finished-history algebra, typed generator combinators, and cheap CI repetition, proptest is superb. For incremental history construction, stateful linearizability prefixes, and rule bodies whose random choices must remain entangled with current model state, Hegel is the more expressive instrument. In a repository that checks linearizability, partition independence, and nondeterministic model behaviour, appreciating the power of both is not compromise; it is architectural honesty.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>The RAII Drop-Guard Pattern in Rust</title>
          <pubDate>Thu, 30 Apr 2026 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://debasishg.github.io/blog/raii-guard/</link>
          <guid>https://debasishg.github.io/blog/raii-guard/</guid>
          <description xml:base="https://debasishg.github.io/blog/raii-guard/">&lt;h1 id=&quot;the-raii-drop-guard-pattern-in-rust&quot;&gt;The RAII Drop-Guard Pattern in Rust&lt;&#x2F;h1&gt;
&lt;h2 id=&quot;context&quot;&gt;Context&lt;&#x2F;h2&gt;
&lt;p&gt;In Rust, any value that owns a resource and frees it in its &lt;code&gt;Drop&lt;&#x2F;code&gt; implementation acts as a &lt;strong&gt;guard&lt;&#x2F;strong&gt;: the resource lives for exactly as long as the value is in scope. This is RAII - &lt;em&gt;Resource Acquisition Is Initialization&lt;&#x2F;em&gt; - and in Rust it is the primary, statically-enforced mechanism for resource management.&lt;&#x2F;p&gt;
&lt;p&gt;A &lt;strong&gt;drop guard&lt;&#x2F;strong&gt; is the pattern of binding such a value to a name whose sole purpose is to keep it in scope. It looks like you&#x27;re creating an unused variable. You&#x27;re not - you&#x27;re creating a lexical region in which a resource is held. The compiler uses the variable&#x27;s scope to schedule the exact point at which the resource is released.&lt;&#x2F;p&gt;
&lt;p&gt;This doc explains the mechanics, why the pattern exists, the benefits it offers, and three concrete examples from real reviews where readers misread the pattern on first contact.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;1-the-rust-mechanics&quot;&gt;1. The Rust mechanics&lt;&#x2F;h2&gt;
&lt;p&gt;There are three syntactic forms people confuse. They behave differently.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;1-1-three-bindings-that-look-similar&quot;&gt;1.1 Three bindings that look similar&lt;&#x2F;h3&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;&#x2F;&#x2F; Form A: bind to a proper name&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;let&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; permit&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; sem&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;acquire&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;()&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;await&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;?&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;&#x2F;&#x2F; Form B: bind to a name starting with underscore&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;let&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; _permit&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; sem&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;acquire&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;()&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;await&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;?&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;&#x2F;&#x2F; Form C: destructure with a bare underscore pattern&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;let&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; _&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; sem&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;acquire&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;()&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;await&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;?&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Semantically:&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Form&lt;&#x2F;th&gt;&lt;th&gt;Binding&lt;&#x2F;th&gt;&lt;th&gt;Drop runs at&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;A - &lt;code&gt;permit&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;yes&lt;&#x2F;td&gt;&lt;td&gt;end of enclosing scope (&lt;code&gt;}&lt;&#x2F;code&gt;)&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;B - &lt;code&gt;_permit&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;yes&lt;&#x2F;td&gt;&lt;td&gt;end of enclosing scope (&lt;code&gt;}&lt;&#x2F;code&gt;)&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;C - &lt;code&gt;_&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;strong&gt;no binding&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;strong&gt;immediately on this statement&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;Forms A and B are identical in terms of when Drop runs. They differ only in how the compiler treats the name for lint purposes:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;permit&lt;&#x2F;code&gt;&lt;&#x2F;strong&gt; - a used-looking name. If you never read it, &lt;code&gt;unused_variables&lt;&#x2F;code&gt; warns.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;_permit&lt;&#x2F;code&gt;&lt;&#x2F;strong&gt; - a name starting with &lt;code&gt;_&lt;&#x2F;code&gt; suppresses &lt;code&gt;unused_variables&lt;&#x2F;code&gt; but is still a real binding. The value is stored; it lives until the scope ends.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;_&lt;&#x2F;code&gt;&lt;&#x2F;strong&gt; (bare) - a &lt;em&gt;pattern&lt;&#x2F;em&gt;, not a name. The value is matched, then immediately dropped. No storage, no name, no lifetime extension.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;This distinction is critical for RAII. If you write &lt;code&gt;let _ = sem.acquire().await?;&lt;&#x2F;code&gt;, you&#x27;ve released the permit on that very line - you held the slot for a nanosecond. If you write &lt;code&gt;let _permit = sem.acquire().await?;&lt;&#x2F;code&gt;, you hold the slot until the closing brace.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;1-2-when-does-drop-actually-run&quot;&gt;1.2 When does Drop actually run?&lt;&#x2F;h3&gt;
&lt;p&gt;Scope exit, in LIFO order of creation. Temporaries within an expression drop at the end of the enclosing statement; &lt;code&gt;let&lt;&#x2F;code&gt;-bound values drop at the end of their block.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;fn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; work&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;() {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;    let&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; a&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Guard&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;new&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;a&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;);&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;       &#x2F;&#x2F; acquired&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;    let&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; b&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Guard&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;new&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;b&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;);&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;       &#x2F;&#x2F; acquired&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;    do_stuff&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;();&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;                    &#x2F;&#x2F; runs with both guards held&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;    &#x2F;&#x2F; implicit: b dropped, then a dropped&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;fn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; work&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;() {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;    {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;        let&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; _inner&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Guard&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;new&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;inner&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;        do_inner&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;();&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;    }&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;                              &#x2F;&#x2F; _inner dropped here&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;    do_outer&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;();&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;                    &#x2F;&#x2F; runs with no guards held&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The compiler inserts the drops at the &lt;code&gt;}&lt;&#x2F;code&gt;. You cannot observe them in the source, but they are deterministic and guaranteed barring abort&#x2F;panic-during-drop pathologies.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;1-3-explicit-release&quot;&gt;1.3 Explicit release&lt;&#x2F;h3&gt;
&lt;p&gt;If you need to release a guard earlier than end-of-scope, call &lt;code&gt;drop(x)&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust z-storage&quot;&gt;let mut&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; guard&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; mutex&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;lock&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;()&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;();&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;modify_protected_state&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-storage&quot;&gt;&amp;amp;mut *&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt;guard);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;drop&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-variable&quot;&gt;(guard);&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;                       &#x2F;&#x2F; release before the function returns&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;expensive_computation_that_doesnt_need_the_lock&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;();&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Or nest a block:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;let&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; result&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;    let&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; _guard&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; mutex&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;lock&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;()&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;();&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;    read_protected_state&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;()&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;};&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;                                 &#x2F;&#x2F; _guard dropped here&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;use&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-variable&quot;&gt;(result);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Both idioms are explicit and legitimate. What you &lt;strong&gt;cannot&lt;&#x2F;strong&gt; do is &quot;forget&quot; to drop - the compiler will drop at scope exit whether you want it to or not, which is the whole point.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;2-what-makes-a-type-a-guard&quot;&gt;2. What makes a type a &quot;guard&quot;&lt;&#x2F;h2&gt;
&lt;p&gt;Any type with a &lt;code&gt;Drop&lt;&#x2F;code&gt; impl that releases a resource is a guard. The Rust ecosystem is full of them:&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Type&lt;&#x2F;th&gt;&lt;th&gt;Resource it guards&lt;&#x2F;th&gt;&lt;th&gt;Who releases on drop&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;std::sync::MutexGuard&amp;lt;&#x27;_, T&amp;gt;&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;an acquired &lt;code&gt;Mutex&lt;&#x2F;code&gt; lock&lt;&#x2F;td&gt;&lt;td&gt;releases the lock&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;tokio::sync::SemaphorePermit&amp;lt;&#x27;_&amp;gt;&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;a semaphore slot&lt;&#x2F;td&gt;&lt;td&gt;releases one permit&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;tempfile::TempDir&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;an on-disk temp directory&lt;&#x2F;td&gt;&lt;td&gt;deletes the directory&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;std::fs::File&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;an OS file descriptor&lt;&#x2F;td&gt;&lt;td&gt;closes the fd&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;Box&amp;lt;T&amp;gt;&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;heap allocation&lt;&#x2F;td&gt;&lt;td&gt;deallocates&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;Vec&amp;lt;T&amp;gt;&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;heap buffer + len&lt;&#x2F;td&gt;&lt;td&gt;deallocates buffer, drops elements&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;A custom &lt;code&gt;struct&lt;&#x2F;code&gt; with manual &lt;code&gt;impl Drop&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;whatever you coded&lt;&#x2F;td&gt;&lt;td&gt;whatever you coded&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;Because the resource lifetime is tied to a value&#x27;s scope, you get deterministic, leak-safe, exception-safe cleanup by construction - without &lt;code&gt;try&#x2F;finally&lt;&#x2F;code&gt;, without &lt;code&gt;defer&lt;&#x2F;code&gt;, without a garbage collector.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;3-three-flavors-of-the-pattern-in-practice&quot;&gt;3. Three flavors of the pattern in practice&lt;&#x2F;h2&gt;
&lt;p&gt;These are three examples that came up in real code review comments on a single PR. Each uses the same underlying Rust mechanic; each looked surprising or wrong to a reviewer encountering it in a context they hadn&#x27;t previously mapped to RAII.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;3-1-semaphore-permits-as-concurrency-gates&quot;&gt;3.1 Semaphore permits as concurrency gates&lt;&#x2F;h3&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;join_set&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;spawn&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;async move&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;    let&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; _global_permit&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; match&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; global_sem&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;acquire&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;()&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;await&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;        Ok&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-variable&quot;&gt;(p)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt; p,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;        Err&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-variable&quot;&gt;(_)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; return&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-variable&quot;&gt; (rid,&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Err&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;sem_closed_error&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;())),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;    };&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;    let&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; _local_permit&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; match&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; local_sem&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;acquire&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;()&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;await&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;        Ok&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-variable&quot;&gt;(p)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt; p,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;        Err&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-variable&quot;&gt;(_)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; return&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-variable&quot;&gt; (rid,&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Err&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;sem_closed_error&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;())),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;    };&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;    let&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; result&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; do_work&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;()&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;await&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-variable&quot;&gt;    (rid, result)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;    &#x2F;&#x2F; _local_permit dropped here → local slot released&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;    &#x2F;&#x2F; _global_permit dropped here → global slot released&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;});&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Two caps - a global one enforced across all spawned groups, a local one per group - are implemented as two semaphores. Each task must hold one permit from each to proceed. The permits are bound to &lt;code&gt;_global_permit&lt;&#x2F;code&gt; and &lt;code&gt;_local_permit&lt;&#x2F;code&gt; so they survive until the task finishes, then release on scope exit.&lt;&#x2F;p&gt;
&lt;p&gt;A reviewer on this PR called the pattern &quot;clever.&quot; What they likely noticed was that two values appear to be &quot;unused&quot; - nothing reads &lt;code&gt;_global_permit&lt;&#x2F;code&gt; or &lt;code&gt;_local_permit&lt;&#x2F;code&gt; - yet the code depends critically on their existence. The &lt;em&gt;existence&lt;&#x2F;em&gt; of the value is the work; the value itself is never consumed. That is the distinctive shape of an RAII guard.&lt;&#x2F;p&gt;
&lt;p&gt;Two things are worth noting beyond the scope-trick itself:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Acquisition order is consistent&lt;&#x2F;strong&gt;: every task acquires global before local. That is deliberate - it&#x27;s the standard anti-deadlock discipline for nested locks. Reverse ordering in some paths (acquire local before global on the error branch, say) would let two tasks each hold one and wait on the other.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Release order is LIFO&lt;&#x2F;strong&gt;: &lt;code&gt;_local_permit&lt;&#x2F;code&gt; drops before &lt;code&gt;_global_permit&lt;&#x2F;code&gt;, matching acquisition. No hand-coded &lt;code&gt;drop()&lt;&#x2F;code&gt; calls are needed.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;3-2-tempdir-as-a-filesystem-lifetime-anchor&quot;&gt;3.2 &lt;code&gt;TempDir&lt;&#x2F;code&gt; as a filesystem lifetime anchor&lt;&#x2F;h3&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;fn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; create_test_workspace&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;()&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; (&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-namespace&quot;&gt;tempfile&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;TempDir&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Workspace&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;    let&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; tmp&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-namespace&quot;&gt; tempfile&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;TempDir&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;new&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;()&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;();&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;    let&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; ws&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Workspace&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;create_at&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-variable&quot;&gt;(tmp&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;path&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(),&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-namespace&quot;&gt; ExecutionStrategy&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;Sequential&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;        .&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;expect&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;Failed to create test workspace&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-variable&quot;&gt;    (tmp, ws)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;#[&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-attribute z-rust&quot;&gt;test&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;fn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; test_something&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;() {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;    let&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-variable&quot;&gt; (_tempdir,&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage&quot;&gt; mut&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt; workspace)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; create_test_workspace&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;();&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    workspace&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;set_session&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&#x2F;github&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-namespace&quot;&gt; SessionId&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;new&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;…&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;into&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;()))&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;();&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    workspace&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;save&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;()&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;();&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;    &#x2F;&#x2F; ...assertions...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;    &#x2F;&#x2F; _tempdir dropped here → directory deleted&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;tempfile::TempDir&lt;&#x2F;code&gt; guards a temporary directory on disk. It deletes the directory when it drops. &lt;code&gt;TempDir::path&lt;&#x2F;code&gt; returns a &lt;code&gt;&amp;amp;Path&lt;&#x2F;code&gt; borrow; the workspace likely converts it to an owned &lt;code&gt;PathBuf&lt;&#x2F;code&gt; internally for storage, but it does not own the &lt;code&gt;TempDir&lt;&#x2F;code&gt; itself - so if the &lt;code&gt;TempDir&lt;&#x2F;code&gt; drops mid-test, subsequent &lt;code&gt;workspace.save()&lt;&#x2F;code&gt; will fail because the backing directory is gone.&lt;&#x2F;p&gt;
&lt;p&gt;So the helper returns &lt;em&gt;both&lt;&#x2F;em&gt; - the &lt;code&gt;Workspace&lt;&#x2F;code&gt; you operate on and the &lt;code&gt;TempDir&lt;&#x2F;code&gt; whose scope must outlive it. The caller binds the &lt;code&gt;TempDir&lt;&#x2F;code&gt; to a name. Any name works, but &lt;code&gt;_tempdir&lt;&#x2F;code&gt; is conventional because:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;It communicates &quot;this is a lifetime anchor, not something you&#x27;ll read.&quot;&lt;&#x2F;li&gt;
&lt;li&gt;It silences the unused-variable lint without the bare &lt;code&gt;_&lt;&#x2F;code&gt; that would drop it immediately.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;A reviewer on this PR objected that &quot;the first element of the tuple isn&#x27;t being used.&quot; The literal claim is a misread - &lt;code&gt;_tempdir&lt;&#x2F;code&gt; &lt;em&gt;is&lt;&#x2F;em&gt; used, via its &lt;code&gt;Drop&lt;&#x2F;code&gt; side-effect, which is the point of the binding. But the misread is informative: the pattern&#x27;s central move (a binding whose value is never read) looks like dead code to readers not tuned to RAII. The cheapest mitigation is naming: &lt;code&gt;_tempdir&lt;&#x2F;code&gt; reads intent better than &lt;code&gt;_tmp&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;3-3-mutexguard-for-scoped-critical-sections&quot;&gt;3.3 &lt;code&gt;MutexGuard&lt;&#x2F;code&gt; for scoped critical sections&lt;&#x2F;h3&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage&quot;&gt;static&lt;&#x2F;span&gt;&lt;span&gt; STATE&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Mutex&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;Counters&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Mutex&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;new&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-namespace&quot;&gt;Counters&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;new&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;());&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;fn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; observe_and_reset&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;()&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Counters&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust z-storage&quot;&gt;    let mut&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; guard&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; STATE&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;lock&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;()&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;();&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;    let&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; snapshot&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; guard&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;clone&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;();&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;    *&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;guard&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Counters&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;new&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;();&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    snapshot&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;    &#x2F;&#x2F; guard dropped here → mutex released&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;MutexGuard&lt;&#x2F;code&gt; is probably the most textbook RAII guard in Rust. Acquired by &lt;code&gt;lock()&lt;&#x2F;code&gt;, released on drop, and &lt;code&gt;Deref&lt;&#x2F;code&gt;s to the protected value. The &lt;code&gt;snapshot&lt;&#x2F;code&gt; returned is derived from the protected state under the lock; the guard releases immediately after the expression &lt;code&gt;snapshot&lt;&#x2F;code&gt; is evaluated, at the end of the function.&lt;&#x2F;p&gt;
&lt;p&gt;If you want to shrink the critical section, nest a block:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;fn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; observe_and_reset&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;()&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Counters&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;    let&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; snapshot&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust z-storage&quot;&gt;        let mut&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; guard&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; STATE&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;lock&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;()&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;();&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;        let&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; s&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; guard&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;clone&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;();&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;        *&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;guard&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Counters&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;new&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;();&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;        s&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;    };&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt; &#x2F;&#x2F; guard dropped here&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;    expensive_post_processing&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt;snapshot);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    snapshot&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Same pattern. The guard anchors the critical section to a lexical region.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;4-why-rust-uses-this-pattern&quot;&gt;4. Why Rust uses this pattern&lt;&#x2F;h2&gt;
&lt;p&gt;Rust deliberately doesn&#x27;t have &lt;code&gt;try&#x2F;finally&lt;&#x2F;code&gt;, &lt;code&gt;defer&lt;&#x2F;code&gt;, or runtime-managed cleanup. RAII via &lt;code&gt;Drop&lt;&#x2F;code&gt; is the uniform mechanism, and that is a design decision worth understanding.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;4-1-static-deterministic-cleanup&quot;&gt;4.1 Static, deterministic cleanup&lt;&#x2F;h3&gt;
&lt;p&gt;The compiler knows when every value&#x27;s scope ends. Drop code is inserted at those points. There is no runtime scheduling, no &quot;when the GC feels like it,&quot; no race between cleanup threads. The same source produces the same drop ordering on every run. This means:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Cleanup cost is accounted for at compile time - you know where the &lt;code&gt;drop()&lt;&#x2F;code&gt; calls are even if they&#x27;re implicit.&lt;&#x2F;li&gt;
&lt;li&gt;Drop order is observable and portable - tests that exercise cleanup side effects are reproducible.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;4-2-exception-safety-is-free&quot;&gt;4.2 Exception-safety is free&lt;&#x2F;h3&gt;
&lt;p&gt;If a function panics mid-way, stack unwinding drops every value in every stack frame being unwound, in reverse order of creation. A &lt;code&gt;MutexGuard&lt;&#x2F;code&gt; held across the panic point releases the lock during unwind. A &lt;code&gt;TempDir&lt;&#x2F;code&gt; deletes itself. A &lt;code&gt;File&lt;&#x2F;code&gt; closes. Contrast with C++ (where you get RAII in destructors but must combine it with careful exception types) or Go (where a forgotten &lt;code&gt;defer&lt;&#x2F;code&gt; leaks silently).&lt;&#x2F;p&gt;
&lt;p&gt;There is no path through a Rust function, however exceptional, that skips Drop. The exceptions are all explicit and named:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;std::process::abort()&lt;&#x2F;code&gt; - terminates without unwinding.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;std::mem::forget()&lt;&#x2F;code&gt; and &lt;code&gt;ManuallyDrop&lt;&#x2F;code&gt; - opt out of running &lt;code&gt;Drop&lt;&#x2F;code&gt; for a specific value.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;Box::leak&lt;&#x2F;code&gt; &#x2F; &lt;code&gt;Vec::leak&lt;&#x2F;code&gt; &#x2F; &lt;code&gt;String::leak&lt;&#x2F;code&gt; - intentionally turn an owned value into a &lt;code&gt;&#x27;static&lt;&#x2F;code&gt; reference, leaking the allocation.&lt;&#x2F;li&gt;
&lt;li&gt;A &lt;code&gt;Drop&lt;&#x2F;code&gt; impl that itself panics during unwinding - aborts the process.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Reference cycles via &lt;code&gt;Rc&lt;&#x2F;code&gt; &#x2F; &lt;code&gt;Arc&lt;&#x2F;code&gt;&lt;&#x2F;strong&gt; - if two &lt;code&gt;Rc&lt;&#x2F;code&gt;s reference each other, neither&#x27;s strong count ever reaches zero, so neither value is dropped. This is memory-safe (no UB, as &lt;code&gt;mem::forget&lt;&#x2F;code&gt; is also memory-safe) but it is the most common &quot;my &lt;code&gt;Drop&lt;&#x2F;code&gt; never ran&quot; footgun in real code. The standard remedy is &lt;code&gt;Weak&lt;&#x2F;code&gt; for the back-edge.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;4-3-coupling-lifetime-to-semantic-scope-not-to-control-flow&quot;&gt;4.3 Coupling lifetime to &lt;em&gt;semantic&lt;&#x2F;em&gt; scope, not to control flow&lt;&#x2F;h3&gt;
&lt;p&gt;The shape of the scope - function body, block, match arm - becomes the shape of the resource&#x27;s lifetime. You don&#x27;t have to hand-thread release calls through every early return, every error branch, every loop break. The compiler does it. That removes a whole class of bugs:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&quot;forgot to unlock on the error path&quot; - impossible; the guard drops on unwind.&lt;&#x2F;li&gt;
&lt;li&gt;&quot;released the permit twice&quot; - can&#x27;t; you can only drop an owned value once.&lt;&#x2F;li&gt;
&lt;li&gt;&quot;used after release&quot; - caught at compile time; the borrow checker rejects uses after &lt;code&gt;drop(guard)&lt;&#x2F;code&gt; or after scope exit.&lt;&#x2F;li&gt;
&lt;li&gt;&quot;resource leaked because of an early return&quot; - the return point runs the implicit drops.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;4-4-composable&quot;&gt;4.4 Composable&lt;&#x2F;h3&gt;
&lt;p&gt;A struct that owns a guard inherits the guard&#x27;s behavior. If &lt;code&gt;MyService&lt;&#x2F;code&gt; holds a &lt;code&gt;MutexGuard&amp;lt;&#x27;_, State&amp;gt;&lt;&#x2F;code&gt;, dropping &lt;code&gt;MyService&lt;&#x2F;code&gt; releases the lock. You build larger transactional units by composition, and Rust&#x27;s drop ordering gives you deterministic control - but note that the rule for &lt;strong&gt;struct fields&lt;&#x2F;strong&gt; differs from the rule for &lt;strong&gt;local &lt;code&gt;let&lt;&#x2F;code&gt; bindings&lt;&#x2F;strong&gt;:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Locals in a block drop in &lt;strong&gt;reverse declaration&lt;&#x2F;strong&gt; order (LIFO), as shown in §1.2.&lt;&#x2F;li&gt;
&lt;li&gt;Struct fields drop in &lt;strong&gt;declaration order&lt;&#x2F;strong&gt; (top to bottom).&lt;&#x2F;li&gt;
&lt;li&gt;Tuple and array elements likewise drop in declaration &#x2F; index order.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;So to release the lock &lt;em&gt;last&lt;&#x2F;em&gt; (after the audit log is finalized and pending entries are released), the lock field must be declared &lt;em&gt;last&lt;&#x2F;em&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;struct&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Transaction&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;a&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    pending&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Vec&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;Entry&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    _log&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; AuditLog&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    _lock&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; MutexGuard&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;a&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Bank&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;&#x2F;&#x2F; Fields drop in declaration order:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;&#x2F;&#x2F;   1. pending drops - entries released&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;&#x2F;&#x2F;   2. _log drops    - audit entry finalized&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;&#x2F;&#x2F;   3. _lock drops   - bank unlocked (released last, as intended)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;&#x2F;&#x2F; To change the order, reorder the fields.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If a struct itself has a manual &lt;code&gt;impl Drop&lt;&#x2F;code&gt;, the compiler runs that &lt;code&gt;drop&lt;&#x2F;code&gt; body first, then drops the fields in declaration order.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;4-5-tooling-recognizes-the-pattern&quot;&gt;4.5 Tooling recognizes the pattern&lt;&#x2F;h3&gt;
&lt;p&gt;Clippy and rustc have lints specifically for drop-guard misuse:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;#[must_use]&lt;&#x2F;code&gt; on &lt;code&gt;MutexGuard&lt;&#x2F;code&gt; &#x2F; &lt;code&gt;SemaphorePermit&lt;&#x2F;code&gt; &#x2F; &lt;code&gt;TempDir&lt;&#x2F;code&gt; makes rustc warn when the value is dropped as a &lt;em&gt;bare expression statement&lt;&#x2F;em&gt; (e.g., &lt;code&gt;mutex.lock();&lt;&#x2F;code&gt; with no binding at all). Note: it does &lt;strong&gt;not&lt;&#x2F;strong&gt; fire for &lt;code&gt;let _ = mutex.lock();&lt;&#x2F;code&gt; - the underscore pattern counts as a use.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;let_underscore_lock&lt;&#x2F;code&gt; (clippy) covers exactly that gap - it warns on &lt;code&gt;let _ = mutex.lock();&lt;&#x2F;code&gt; because the bare &lt;code&gt;_&lt;&#x2F;code&gt; pattern drops the guard immediately, almost always a bug.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;let_underscore_must_use&lt;&#x2F;code&gt; (clippy) is the more general form: warns when &lt;code&gt;let _&lt;&#x2F;code&gt; discards any &lt;code&gt;#[must_use]&lt;&#x2F;code&gt; value.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;used_underscore_binding&lt;&#x2F;code&gt; (clippy) warns if a &lt;code&gt;_name&lt;&#x2F;code&gt; binding is actually read (reasonably - &lt;code&gt;_name&lt;&#x2F;code&gt; signals intent-not-to-read).&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The ecosystem is aware of the pattern and the failure modes adjacent to it.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;5-benefits-restated-as-a-checklist&quot;&gt;5. Benefits, restated as a checklist&lt;&#x2F;h2&gt;
&lt;p&gt;When you reach for an RAII guard over an explicit acquire&#x2F;release pair, you get:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Cleanup on every path, including panics.&lt;&#x2F;strong&gt; No &lt;code&gt;try&#x2F;finally&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;One acquisition site, one name, one scope.&lt;&#x2F;strong&gt; No &quot;did I call release on every branch?&quot;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Static drop-order guarantees.&lt;&#x2F;strong&gt; No runtime reordering; no surprises.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Composable resource ownership.&lt;&#x2F;strong&gt; Guards nest into structs, get returned from functions, and still respect their scope. (Caveat for async: a &lt;code&gt;std::sync::MutexGuard&lt;&#x2F;code&gt; is &lt;code&gt;!Send&lt;&#x2F;code&gt; and cannot be held across an &lt;code&gt;.await&lt;&#x2F;code&gt; in a &lt;code&gt;Send&lt;&#x2F;code&gt; future. Even when the type system permits it, holding a sync mutex across &lt;code&gt;.await&lt;&#x2F;code&gt; is almost always a bug - it can stall the runtime. Clippy&#x27;s &lt;code&gt;await_holding_lock&lt;&#x2F;code&gt; flags this. Use &lt;code&gt;tokio::sync::Mutex&lt;&#x2F;code&gt; if a guard genuinely needs to live across an await point.)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Lint-enforced correctness.&lt;&#x2F;strong&gt; The compiler and clippy catch the common mistakes.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Self-documenting lifetime.&lt;&#x2F;strong&gt; The region where the resource is held is visible as a block or a function body - no reader has to trace control flow to find the release.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;The cost, compared to a &quot;just call &lt;code&gt;release()&lt;&#x2F;code&gt; manually&quot; approach, is essentially zero at runtime and slightly more vocabulary at read time: you must recognize that &lt;code&gt;_guard&lt;&#x2F;code&gt; is a real binding, that Drop runs at the &lt;code&gt;}&lt;&#x2F;code&gt;, and that a bare &lt;code&gt;_&lt;&#x2F;code&gt; is different from &lt;code&gt;_name&lt;&#x2F;code&gt;. Once internalized, the pattern fades into background idiom.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;6-common-misreadings&quot;&gt;6. Common misreadings&lt;&#x2F;h2&gt;
&lt;p&gt;These are the shapes that trip readers who haven&#x27;t fully absorbed the pattern. Each has a cheap remedy.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;misreading-1-this-variable-is-unused&quot;&gt;Misreading 1: &quot;This variable is unused.&quot;&lt;&#x2F;h3&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;let&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; _permit&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; sem&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;acquire&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;()&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;await&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;?&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The name starts with &lt;code&gt;_&lt;&#x2F;code&gt;, there is no read of &lt;code&gt;_permit&lt;&#x2F;code&gt; anywhere, the linter is silent. To a reader used to scanning for reads, the binding looks dead. They may suggest removing it or changing it to &lt;code&gt;let _ = sem.acquire().await?;&lt;&#x2F;code&gt;. &lt;strong&gt;Both changes break the program&lt;&#x2F;strong&gt; - the permit would release immediately.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Remedy:&lt;&#x2F;strong&gt; give the binding a name that telegraphs &lt;em&gt;intent to hold&lt;&#x2F;em&gt;, and&#x2F;or add a one-line comment the first time the pattern appears in a file.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;let&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; _permit&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; sem&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;acquire&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;()&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;await&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;?&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt; &#x2F;&#x2F; RAII guard – keeps permit alive until end of task&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Or, for strictly lifetime-anchoring values:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;let&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; _permit_keep_alive&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; sem&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;acquire&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;()&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;await&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;?&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h3 id=&quot;misreading-2-drop-isn-t-happening-where-i-expect&quot;&gt;Misreading 2: &quot;Drop isn&#x27;t happening where I expect.&quot;&lt;&#x2F;h3&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;fn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; work&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;() {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;    let&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; _guard&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; mutex&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;lock&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;()&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;();&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;    &#x2F;&#x2F; ...20 lines...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;    expensive_pure_computation&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;();&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;   &#x2F;&#x2F; still holds the mutex!&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;_guard&lt;&#x2F;code&gt; drops at the end of the function, not at the end of the work that actually needs it. A reviewer might assume the mutex is released before &lt;code&gt;expensive_pure_computation&lt;&#x2F;code&gt;; it isn&#x27;t.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Remedy:&lt;&#x2F;strong&gt; use explicit &lt;code&gt;drop(_guard);&lt;&#x2F;code&gt;, or nest a block to bound the critical section:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;fn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; work&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;() {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;    {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;        let&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; _guard&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; mutex&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;lock&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;()&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;();&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;        protected_step&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;();&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;    expensive_pure_computation&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;();&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;   &#x2F;&#x2F; now runs unlocked&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h3 id=&quot;misreading-3-the-first-tuple-element-isn-t-used&quot;&gt;Misreading 3: &quot;The first tuple element isn&#x27;t used.&quot;&lt;&#x2F;h3&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;let&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-variable&quot;&gt; (_tempdir, workspace)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; create_test_workspace&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;();&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This is misreading 1 in disguise, but dressed up as a tuple destructure. The first element is a &lt;code&gt;TempDir&lt;&#x2F;code&gt; whose &lt;code&gt;Drop&lt;&#x2F;code&gt; impl deletes the on-disk directory. Binding it to &lt;code&gt;_tempdir&lt;&#x2F;code&gt; keeps the directory alive for the test body. Destructuring with &lt;code&gt;let (_, workspace) = …&lt;&#x2F;code&gt; would delete the directory immediately and break every subsequent I&#x2F;O operation on &lt;code&gt;workspace&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Remedy:&lt;&#x2F;strong&gt; the name. &lt;code&gt;_tempdir&lt;&#x2F;code&gt; reads better than &lt;code&gt;_tmp&lt;&#x2F;code&gt;; a doc comment on the helper that returns the tuple seals the intent.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;misreading-4-just-use-drop-at-the-right-place-guards-are-magic&quot;&gt;Misreading 4: &quot;Just use &lt;code&gt;drop()&lt;&#x2F;code&gt; at the right place - guards are magic.&quot;&lt;&#x2F;h3&gt;
&lt;p&gt;The concern here is that Drop at scope exit is &quot;implicit&quot; and therefore opaque. In practice the placement is predictable (closing brace of the enclosing block) and tools can show you: &lt;code&gt;rust-analyzer&lt;&#x2F;code&gt;&#x27;s &quot;inlay hints&quot; will display drop points; &lt;code&gt;cargo clippy&lt;&#x2F;code&gt; catches the main misuse shapes. You don&#x27;t need to treat Drop as magic - you need to treat it as a compile-time scheduling rule.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;7-when-to-use-the-pattern-and-when-not-to&quot;&gt;7. When to use the pattern - and when not to&lt;&#x2F;h2&gt;
&lt;p&gt;Use a drop guard when:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;You hold a resource whose release is deterministic and tied to a lexical region.&lt;&#x2F;li&gt;
&lt;li&gt;The release is infallible or best-effort (dropping a &lt;code&gt;MutexGuard&lt;&#x2F;code&gt; can&#x27;t fail meaningfully; deleting a &lt;code&gt;TempDir&lt;&#x2F;code&gt; is best-effort - errors are swallowed).&lt;&#x2F;li&gt;
&lt;li&gt;You want the release to happen on every exit path, including panic, without threading cleanup calls through every branch.&lt;&#x2F;li&gt;
&lt;li&gt;You want the guard to compose into larger owned units (structs, tuples, closures captured by &lt;code&gt;move&lt;&#x2F;code&gt;).&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Avoid the pattern or supplement it when:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;The release is fallible in a way the caller must observe. &lt;code&gt;Drop&lt;&#x2F;code&gt; cannot return errors and cannot be &lt;code&gt;await&lt;&#x2F;code&gt;ed. Flushing a buffered writer, for instance, should usually be explicit (&lt;code&gt;writer.flush()?&lt;&#x2F;code&gt;) with the guard as a safety net for the panic path - not as the primary mechanism. &lt;code&gt;BufWriter&lt;&#x2F;code&gt;&#x27;s drop flushes best-effort and ignores errors.&lt;&#x2F;li&gt;
&lt;li&gt;The resource needs async cleanup. &lt;code&gt;Drop&lt;&#x2F;code&gt; is synchronous. For async cleanup, use explicit &lt;code&gt;close().await&lt;&#x2F;code&gt; and let the guard handle only the emergency-panic path. The &lt;code&gt;AsyncDrop&lt;&#x2F;code&gt; RFCs are still in flux.&lt;&#x2F;li&gt;
&lt;li&gt;The scope-exit timing is wrong. If the &quot;natural&quot; scope is larger than the time you want to hold the resource, use &lt;code&gt;drop(x)&lt;&#x2F;code&gt; or a nested block.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;For ad-hoc &quot;run this on scope exit&quot; needs that don&#x27;t justify a dedicated type - restoring some thread-local, undoing a temporary state mutation in a test - the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.rs&#x2F;scopeguard&quot;&gt;&lt;code&gt;scopeguard&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; crate provides &lt;code&gt;defer!&lt;&#x2F;code&gt; and &lt;code&gt;defer_on_unwind!&lt;&#x2F;code&gt; macros. They are RAII guards underneath (a generated struct with a &lt;code&gt;Drop&lt;&#x2F;code&gt; impl) and complement, rather than replace, named guard types: reach for &lt;code&gt;defer!&lt;&#x2F;code&gt; when the cleanup is one-off and inline; reach for a named guard when the resource has a real type that consumers should see in signatures.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;8-quick-reference&quot;&gt;8. Quick reference&lt;&#x2F;h2&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;&#x2F;&#x2F; Standard: bind, use implicitly via Drop, scope-exit releases.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;let&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; _permit&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; sem&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;acquire&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;()&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;await&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;?&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;work&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;()&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;await&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;&#x2F;&#x2F; drop here&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;&#x2F;&#x2F; Explicit early release.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;let&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; permit&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; sem&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;acquire&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;()&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;await&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;?&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;hot_path&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;()&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;await&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;drop&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-variable&quot;&gt;(permit);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;cold_path&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;()&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;await&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;&#x2F;&#x2F; Nested scope to bound release.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;let&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; result&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;    let&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; _guard&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; mutex&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;lock&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;()&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;();&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;    compute_under_lock&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;()&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;};&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;&#x2F;&#x2F; _guard released here&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;use_result&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-variable&quot;&gt;(result);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;&#x2F;&#x2F; Tuple-returning helper with an anchor.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;let&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-variable&quot;&gt; (_tempdir, workspace)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; make_workspace&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;();&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;&#x2F;&#x2F; _tempdir lives as long as this frame; don&amp;#39;t rebind to `_`.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;&#x2F;&#x2F; WRONG: immediate drop.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;let&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; _&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; sem&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;acquire&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;()&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;await&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;?&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;           &#x2F;&#x2F; permit dropped NOW&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;let&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; _&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; mutex&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;lock&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;()&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;();&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;          &#x2F;&#x2F; unlocked NOW&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;let&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; _&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; TempDir&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;new&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;()&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;();&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;        &#x2F;&#x2F; directory deleted NOW&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;9-further-reading&quot;&gt;9. Further reading&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;Rust reference on drop order: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;reference&#x2F;destructors.html&quot;&gt;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;reference&#x2F;destructors.html&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;std::mem::drop&lt;&#x2F;code&gt; and destructor semantics: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;std&#x2F;ops&#x2F;trait.Drop.html&quot;&gt;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;std&#x2F;ops&#x2F;trait.Drop.html&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Tokio &lt;code&gt;Semaphore&lt;&#x2F;code&gt; - &quot;Using a semaphore as a limiter&quot; example: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.rs&#x2F;tokio&#x2F;latest&#x2F;tokio&#x2F;sync&#x2F;struct.Semaphore.html&quot;&gt;https:&#x2F;&#x2F;docs.rs&#x2F;tokio&#x2F;latest&#x2F;tokio&#x2F;sync&#x2F;struct.Semaphore.html&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;tempfile::TempDir&lt;&#x2F;code&gt; - guarantees and pitfalls: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.rs&#x2F;tempfile&#x2F;latest&#x2F;tempfile&#x2F;struct.TempDir.html&quot;&gt;https:&#x2F;&#x2F;docs.rs&#x2F;tempfile&#x2F;latest&#x2F;tempfile&#x2F;struct.TempDir.html&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Clippy lint &lt;code&gt;let_underscore_lock&lt;&#x2F;code&gt;: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;rust-lang.github.io&#x2F;rust-clippy&#x2F;master&#x2F;#let_underscore_lock&quot;&gt;https:&#x2F;&#x2F;rust-lang.github.io&#x2F;rust-clippy&#x2F;master&#x2F;#let_underscore_lock&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Article: &quot;Using &lt;code&gt;Drop&lt;&#x2F;code&gt; to write idiomatic Rust&quot; by Jon Gjengset and others - covers the pattern under the name &quot;drop guards.&quot;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;takeaway&quot;&gt;Takeaway&lt;&#x2F;h2&gt;
&lt;p&gt;A &lt;code&gt;let _name = value;&lt;&#x2F;code&gt; binding where &lt;code&gt;value&lt;&#x2F;code&gt; has a meaningful &lt;code&gt;Drop&lt;&#x2F;code&gt; is not an &quot;unused variable.&quot; It is a &lt;strong&gt;lexical declaration of a resource&#x27;s lifetime.&lt;&#x2F;strong&gt; The value&#x27;s type - &lt;code&gt;MutexGuard&lt;&#x2F;code&gt;, &lt;code&gt;SemaphorePermit&lt;&#x2F;code&gt;, &lt;code&gt;TempDir&lt;&#x2F;code&gt;, &lt;code&gt;File&lt;&#x2F;code&gt;, custom &lt;code&gt;Drop&lt;&#x2F;code&gt; impl - tells you what&#x27;s being held. The scope - function body, block, match arm - tells you for how long. The scope&#x27;s &lt;code&gt;}&lt;&#x2F;code&gt; is the release point.&lt;&#x2F;p&gt;
&lt;p&gt;Once that triad (type, scope, brace) is legible to a reader, the pattern stops being &quot;clever&quot; and becomes background mechanism. Until then, the cheapest thing to do in a codebase is name the bindings well (&lt;code&gt;_tempdir&lt;&#x2F;code&gt;, &lt;code&gt;_permit&lt;&#x2F;code&gt;, &lt;code&gt;_guard&lt;&#x2F;code&gt;) and document the pattern at its first load-bearing use in the file.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Dyn-Compatible Async Traits in Rust: Why the Manual Boxed Future Idiom is Required</title>
          <pubDate>Sun, 26 Apr 2026 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://debasishg.github.io/blog/pin-dyn/</link>
          <guid>https://debasishg.github.io/blog/pin-dyn/</guid>
          <description xml:base="https://debasishg.github.io/blog/pin-dyn/">&lt;h1 id=&quot;pin-box-dyn-future-send-in-traits-why-the-manual-desugar&quot;&gt;&lt;code&gt;Pin&amp;lt;Box&amp;lt;dyn Future + Send&amp;gt;&amp;gt;&lt;&#x2F;code&gt; in Traits - Why the Manual Desugar?&lt;&#x2F;h1&gt;
&lt;h2 id=&quot;context&quot;&gt;Context&lt;&#x2F;h2&gt;
&lt;p&gt;When a Rust trait needs async methods &lt;strong&gt;and&lt;&#x2F;strong&gt; must be usable as a trait object (&lt;code&gt;Box&amp;lt;dyn Trait&amp;gt;&lt;&#x2F;code&gt;, &lt;code&gt;&amp;amp;dyn Trait&lt;&#x2F;code&gt;, &lt;code&gt;Arc&amp;lt;dyn Trait&amp;gt;&lt;&#x2F;code&gt;), you cannot just write &lt;code&gt;async fn&lt;&#x2F;code&gt; and move on. &lt;code&gt;async fn&lt;&#x2F;code&gt; in traits returns an &lt;strong&gt;opaque&lt;&#x2F;strong&gt; &lt;code&gt;impl Future&lt;&#x2F;code&gt; whose concrete type differs per implementation - which breaks vtable dispatch. The compiler&#x27;s own error-recovery hint spells it out:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;to make this trait dyn-compatible, use &lt;code&gt;-&amp;gt; Pin&amp;lt;Box&amp;lt;dyn Future&amp;lt;Output = ...&amp;gt; + Send&amp;gt;&amp;gt;&lt;&#x2F;code&gt; instead.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;The manual form - write a synchronous fn returning &lt;code&gt;Pin&amp;lt;Box&amp;lt;dyn Future&amp;lt;Output = T&amp;gt; + Send&amp;gt;&amp;gt;&lt;&#x2F;code&gt;, implement it with &lt;code&gt;Box::pin(async move { ... })&lt;&#x2F;code&gt; - exists to preserve dyn-compatibility. It is what &lt;code&gt;#[async_trait]&lt;&#x2F;code&gt; generates under the hood. It is what &lt;code&gt;tower::Service&lt;&#x2F;code&gt;, &lt;code&gt;hyper::Service&lt;&#x2F;code&gt;, and many service-oriented abstractions use (sometimes with an associated future type, sometimes with the boxed form). It looks unfamiliar at first; it is mechanically load-bearing.&lt;&#x2F;p&gt;
&lt;p&gt;This doc explains why the pattern exists, what it gives you, and the lifetime subtlety that turns every &quot;clone Arc fields before &lt;code&gt;async move&lt;&#x2F;code&gt;&quot; line in the implementation into a consequence of the trait&#x27;s signature.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;1-the-setup-async-polymorphism-pluggable-backends&quot;&gt;1. The setup: async + polymorphism + pluggable backends&lt;&#x2F;h2&gt;
&lt;p&gt;A common design:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;You define a trait that abstracts some capability with async methods - an executor, a service, a storage backend, a message bus.&lt;&#x2F;li&gt;
&lt;li&gt;Production uses one implementation (a real HTTP executor, a real database backend).&lt;&#x2F;li&gt;
&lt;li&gt;Tests use another (a mock that records calls and returns canned responses).&lt;&#x2F;li&gt;
&lt;li&gt;Consumers of the trait want to swap between them at construction time without recompiling.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The most natural Rust encoding is:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;pub&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt; struct&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Dispatcher&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    executor&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Box&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;dyn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; StepExecutor&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;   &#x2F;&#x2F; production or mock, decided at construction&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;For this to compile, &lt;code&gt;StepExecutor&lt;&#x2F;code&gt; must be &lt;strong&gt;dyn-compatible&lt;&#x2F;strong&gt; (the compiler&#x27;s current term for what used to be called &quot;object-safe&quot;). That is: you can build a vtable for it, because every method&#x27;s signature is known at compile time regardless of the &lt;code&gt;Self&lt;&#x2F;code&gt; type that implements it.&lt;&#x2F;p&gt;
&lt;p&gt;Synchronous methods are trivially dyn-compatible. Async methods are not - and that is where the whole pattern starts.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;2-what-async-fn-actually-is&quot;&gt;2. What &lt;code&gt;async fn&lt;&#x2F;code&gt; actually is&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;code&gt;async fn foo(...) -&amp;gt; T { body }&lt;&#x2F;code&gt; is sugar for:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;fn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; foo&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;...&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; impl&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Future&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;Output&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; T&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;gt; {&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; async move&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-variable&quot;&gt; { body } }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The return type is &lt;code&gt;impl Future&amp;lt;Output = T&amp;gt;&lt;&#x2F;code&gt; - an &lt;strong&gt;anonymous existential type&lt;&#x2F;strong&gt; whose concrete identity is &quot;whatever compiler-generated state machine this particular function body produced.&quot; The caller gets something that implements &lt;code&gt;Future&lt;&#x2F;code&gt;, but the caller does not know (and cannot name) the concrete type (that&#x27;s what existential types are for).&lt;&#x2F;p&gt;
&lt;p&gt;This is excellent for monomorphized, generic code. The compiler specializes each call site against the concrete future type and inlines aggressively.&lt;&#x2F;p&gt;
&lt;p&gt;It is fatal for dyn dispatch.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;2-1-why-impl-future-and-dyn-dispatch-are-incompatible&quot;&gt;2.1 Why &lt;code&gt;impl Future&lt;&#x2F;code&gt; and dyn dispatch are incompatible&lt;&#x2F;h3&gt;
&lt;p&gt;A vtable stores function pointers with fixed signatures. For a sync method &lt;code&gt;fn run(&amp;amp;self, x: u32) -&amp;gt; bool&lt;&#x2F;code&gt;, the vtable slot is a pointer to a function taking &lt;code&gt;(&amp;amp;dyn Trait, u32)&lt;&#x2F;code&gt; and returning &lt;code&gt;bool&lt;&#x2F;code&gt;. Every implementation of the trait must produce a function with that exact signature.&lt;&#x2F;p&gt;
&lt;p&gt;For &lt;code&gt;fn run(&amp;amp;self, x: u32) -&amp;gt; impl Future&amp;lt;Output = bool&amp;gt;&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Implementation A&#x27;s &lt;code&gt;impl Future&lt;&#x2F;code&gt; is some compiler-generated &lt;code&gt;Running&amp;lt;A&amp;gt;&lt;&#x2F;code&gt; state machine.&lt;&#x2F;li&gt;
&lt;li&gt;Implementation B&#x27;s &lt;code&gt;impl Future&lt;&#x2F;code&gt; is a different &lt;code&gt;Running&amp;lt;B&amp;gt;&lt;&#x2F;code&gt; state machine with a different size, different layout, and different &lt;code&gt;poll&lt;&#x2F;code&gt; code.&lt;&#x2F;li&gt;
&lt;li&gt;They are &lt;strong&gt;not the same type&lt;&#x2F;strong&gt;. There is no single function signature the vtable can store.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;You cannot put &lt;code&gt;Running&amp;lt;A&amp;gt;&lt;&#x2F;code&gt; and &lt;code&gt;Running&amp;lt;B&amp;gt;&lt;&#x2F;code&gt; behind the same function pointer. Polymorphism through dispatch requires a stable, type-erased return type. &lt;code&gt;impl Future&lt;&#x2F;code&gt; is the opposite of type-erased.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;2-2-what-the-compiler-does-today-rust-1-75&quot;&gt;2.2 What the compiler does today (Rust 1.75+)&lt;&#x2F;h3&gt;
&lt;p&gt;You &lt;em&gt;can&lt;&#x2F;em&gt; write &lt;code&gt;async fn&lt;&#x2F;code&gt; in traits. The compiler added that support in Rust 1.75 (late 2023). But the same method on &lt;code&gt;dyn Trait&lt;&#x2F;code&gt; is rejected with an error that explicitly instructs you to switch to the manual form.&lt;&#x2F;p&gt;
&lt;p&gt;Consider this minimal example - a trait with one &lt;code&gt;async fn&lt;&#x2F;code&gt;, then an attempt to use it behind a trait object:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;type&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Result&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;T&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-namespace&quot;&gt; std&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-namespace&quot;&gt;result&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;Result&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;T&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Box&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;dyn&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; std&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;error&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;Error&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;gt;&amp;gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;pub&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt; trait&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; StepExecutor&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    async fn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; execute&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language z-self&quot;&gt;self&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Result&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;()&amp;gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;fn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; build&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;()&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Box&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;dyn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; StepExecutor&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;gt; {&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; todo!&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;() }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The trait declaration compiles fine on its own. The line &lt;code&gt;Box&amp;lt;dyn StepExecutor&amp;gt;&lt;&#x2F;code&gt; is what trips the dyn-compatibility check, producing:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;error[E0038]: the trait `StepExecutor` is not dyn compatible&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  --&amp;gt; src&#x2F;lib.rs:10:18&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;   |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;10 | fn build() -&amp;gt; Box&amp;lt;dyn StepExecutor&amp;gt; { todo!() }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;   |                  ^^^^^^^^^^^^^^^^^ `StepExecutor` is not dyn compatible&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;   |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;note: for a trait to be dyn compatible it needs to allow building a vtable&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;help: consider boxing the future:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;   |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;-    async fn execute(&amp;amp;self) -&amp;gt; Result&amp;lt;()&amp;gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;+    fn execute(&amp;amp;self) -&amp;gt; Pin&amp;lt;Box&amp;lt;dyn Future&amp;lt;Output = Result&amp;lt;()&amp;gt;&amp;gt; + Send + &amp;#39;_&amp;gt;&amp;gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The compiler is telling you the lowered form. Adopting it is &lt;em&gt;the&lt;&#x2F;em&gt; way to keep dyn compatibility.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;3-the-manual-form-line-by-line&quot;&gt;3. The manual form, line by line&lt;&#x2F;h2&gt;
&lt;p&gt;Here is the paired trait declaration and impl, written the manual way:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;&#x2F;&#x2F; Trait declaration&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;pub&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt; trait&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; StepExecutor&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Send&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; +&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Sync&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    fn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; execute_prepared&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;        &amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language z-self&quot;&gt;self&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;        requests&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Vec&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;PreparedRequest&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;        max_concurrency&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; usize&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;    )&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Pin&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;Box&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;dyn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Future&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;Output&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Vec&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;Result&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;ExecutionResult&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; +&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Send&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;gt;&amp;gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;&#x2F;&#x2F; Implementation&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;impl&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; StepExecutor&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; for&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; ParallelExecutor&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    fn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; execute_prepared&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;        &amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language z-self&quot;&gt;self&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;        requests&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Vec&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;PreparedRequest&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;        max_concurrency&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; usize&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;    )&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Pin&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;Box&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;dyn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Future&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;Output&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Vec&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;Result&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;ExecutionResult&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; +&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Send&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;gt;&amp;gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;        let&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; executor&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language z-self&quot;&gt; self&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;http_executor&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;clone&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;();&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;     &#x2F;&#x2F; (1) snapshot fields&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;        let&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; handler&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;  =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language z-self&quot;&gt; self&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;adapter_handler&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;clone&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;();&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;   &#x2F;&#x2F;     before the async block&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;        Box&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;pin&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;async move&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;                          &#x2F;&#x2F; (2) build the future&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;            let&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; sem&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-namespace&quot;&gt; Arc&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;new&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-namespace&quot;&gt;Semaphore&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;new&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-variable&quot;&gt;(max_concurrency&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;max&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric&quot;&gt;1&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)));&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust z-storage&quot;&gt;            let mut&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; set&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-namespace&quot;&gt; JoinSet&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;new&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;();&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;            for&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; req&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; in&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt; requests {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;                let&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; exec&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; executor&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;clone&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;();&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;                let&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; hdl&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;  =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; handler&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;clone&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;();&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;                let&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; s&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;    =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; sem&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;clone&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;();&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;                set&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;spawn&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;async move&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;                    let&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; _p&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; s&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;acquire&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;()&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;await&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;ok&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;()&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;?&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;                    exec&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;dispatch&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-variable&quot;&gt;(req, hdl)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;await&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;                });&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;            }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;            set&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;join_all&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;()&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;await&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;        })&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;                                             &#x2F;&#x2F; (3) return the pinned box&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;What each piece does:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;(1) Clone fields before &lt;code&gt;async move&lt;&#x2F;code&gt;.&lt;&#x2F;strong&gt; The returned future is &lt;code&gt;Send + &#x27;static&lt;&#x2F;code&gt;. By the &lt;strong&gt;default object bounds&lt;&#x2F;strong&gt; rule (Rust Reference, &lt;em&gt;Trait object types&lt;&#x2F;em&gt;), when a &lt;code&gt;dyn Trait&lt;&#x2F;code&gt; appears inside &lt;code&gt;Box&amp;lt;…&amp;gt;&lt;&#x2F;code&gt;, &lt;code&gt;Rc&amp;lt;…&amp;gt;&lt;&#x2F;code&gt;, or &lt;code&gt;Arc&amp;lt;…&amp;gt;&lt;&#x2F;code&gt; without an explicit lifetime, the bound defaults to &lt;code&gt;&#x27;static&lt;&#x2F;code&gt;. So &lt;code&gt;Pin&amp;lt;Box&amp;lt;dyn Future&amp;lt;Output = …&amp;gt; + Send&amp;gt;&amp;gt;&lt;&#x2F;code&gt; is shorthand for &lt;code&gt;Pin&amp;lt;Box&amp;lt;dyn Future&amp;lt;Output = …&amp;gt; + Send + &#x27;static&amp;gt;&amp;gt;&lt;&#x2F;code&gt;. &lt;code&gt;async move&lt;&#x2F;code&gt; captures everything it references by move. If it captured &lt;code&gt;&amp;amp;self.http_executor&lt;&#x2F;code&gt;, the future would need to outlive &lt;code&gt;self&lt;&#x2F;code&gt;, which means its type would need a lifetime parameter (&lt;code&gt;+ &#x27;_&lt;&#x2F;code&gt;). The trait said no such parameter. So the body cannot touch &lt;code&gt;self&lt;&#x2F;code&gt; directly - it must capture owned clones of Arc-wrapped fields.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;(2) &lt;code&gt;Box::pin(async move { ... })&lt;&#x2F;code&gt;.&lt;&#x2F;strong&gt; The &lt;code&gt;async move { ... }&lt;&#x2F;code&gt; block produces some compiler-generated future type with the moved-in captures. &lt;code&gt;Box::pin&lt;&#x2F;code&gt; heap-allocates it and pins it - pinning is required because futures may self-reference (an await point can borrow locals that live in the future&#x27;s state machine), and moving such a future invalidates those borrows. Pinning prevents the move. Once allocated and pinned, we erase the concrete type by coercing to &lt;code&gt;Pin&amp;lt;Box&amp;lt;dyn Future&amp;lt;Output = T&amp;gt; + Send&amp;gt;&amp;gt;&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;(3) The return type unifies every impl&#x27;s future.&lt;&#x2F;strong&gt; &lt;code&gt;ParallelExecutor::execute_prepared&lt;&#x2F;code&gt; returns one heap-allocated, pinned future. &lt;code&gt;MockStepExecutor::execute_prepared&lt;&#x2F;code&gt; returns a different heap-allocated, pinned future. To the caller - through &lt;code&gt;dyn StepExecutor&lt;&#x2F;code&gt; - they both look like &lt;code&gt;Pin&amp;lt;Box&amp;lt;dyn Future&amp;lt;Output = ...&amp;gt; + Send&amp;gt;&amp;gt;&lt;&#x2F;code&gt;. The vtable stores one function pointer; each impl&#x27;s pointer builds and returns a different underlying future, but all wearing the same type-erased wrapper.&lt;&#x2F;p&gt;
&lt;p&gt;That is the whole trick. The return type is the lingua franca that makes dispatch possible.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;4-why-the-clone-dance-is-load-bearing-not-just-style&quot;&gt;4. Why the &lt;code&gt;.clone()&lt;&#x2F;code&gt; dance is load-bearing, not just style&lt;&#x2F;h2&gt;
&lt;p&gt;The trait signature controls what the returned future may and may not do. Two constraints matter:&lt;&#x2F;p&gt;
&lt;h3 id=&quot;4-1-send&quot;&gt;4.1 &lt;code&gt;+ Send&lt;&#x2F;code&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;The future will cross thread boundaries - it will be spawned on a tokio runtime, passed to a &lt;code&gt;JoinSet&lt;&#x2F;code&gt;, &lt;code&gt;select!&lt;&#x2F;code&gt;-ed against other futures, etc. &lt;code&gt;Send&lt;&#x2F;code&gt; is mandatory for that to be sound. Every value captured by the &lt;code&gt;async move&lt;&#x2F;code&gt; must itself be &lt;code&gt;Send&lt;&#x2F;code&gt;. If &lt;code&gt;self.http_executor&lt;&#x2F;code&gt; is &lt;code&gt;Arc&amp;lt;HttpExecutor&amp;gt;&lt;&#x2F;code&gt; and &lt;code&gt;HttpExecutor: Send + Sync&lt;&#x2F;code&gt;, then &lt;code&gt;Arc&amp;lt;HttpExecutor&amp;gt;: Send&lt;&#x2F;code&gt;, and cloning it into a local gives you a &lt;code&gt;Send&lt;&#x2F;code&gt; owned handle. If you tried to capture &lt;code&gt;&amp;amp;self.http_executor&lt;&#x2F;code&gt;, you&#x27;d capture a &lt;code&gt;&amp;amp;HttpExecutor&lt;&#x2F;code&gt; - which is &lt;code&gt;Send&lt;&#x2F;code&gt; only if &lt;code&gt;HttpExecutor: Sync&lt;&#x2F;code&gt;, and even then you&#x27;d tie the future&#x27;s lifetime to &lt;code&gt;self&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;4-2-static-implicit-in-the-absence-of&quot;&gt;4.2 &lt;code&gt;&#x27;static&lt;&#x2F;code&gt; (implicit in the absence of &lt;code&gt;+ &#x27;_&lt;&#x2F;code&gt;)&lt;&#x2F;h3&gt;
&lt;p&gt;Inside &lt;code&gt;Pin&amp;lt;Box&amp;lt;dyn Future&amp;lt;Output = T&amp;gt; + Send&amp;gt;&amp;gt;&lt;&#x2F;code&gt;, the trait-object bound is &lt;code&gt;&#x27;static&lt;&#x2F;code&gt; by default. There is no lifetime parameter on the return type. The future must not contain any borrows that outlive the enclosing function - it must own all its captures.&lt;&#x2F;p&gt;
&lt;p&gt;If you write:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;fn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; execute_prepared&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language z-self&quot;&gt;self&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; ...&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Pin&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;Box&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;dyn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Future&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;Output&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; T&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; +&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Send&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;gt;&amp;gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;    Box&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;pin&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;async move&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;        do_work&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language z-self&quot;&gt;self&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function&quot;&gt;http_executor&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;await&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;  &#x2F;&#x2F; captures &amp;amp;self!&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;    })&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The compiler rejects this: &lt;code&gt;self&lt;&#x2F;code&gt; has an anonymous lifetime bounded by the call, but the returned future needs &lt;code&gt;&#x27;static&lt;&#x2F;code&gt;. The error will be some variant of &quot;&lt;code&gt;self&lt;&#x2F;code&gt; does not live long enough&quot; or &quot;captured variable cannot escape &lt;code&gt;FnMut&lt;&#x2F;code&gt; closure body&quot; or &quot;argument requires that &lt;code&gt;&#x27;1&lt;&#x2F;code&gt; must outlive &lt;code&gt;&#x27;static&lt;&#x2F;code&gt;,&quot; depending on the exact shape.&lt;&#x2F;p&gt;
&lt;p&gt;The fix is to replace the borrow with an owned clone:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;fn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; execute_prepared&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language z-self&quot;&gt;self&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; ...&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Pin&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;Box&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;dyn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Future&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;Output&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; T&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; +&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Send&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;gt;&amp;gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;    let&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; executor&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language z-self&quot;&gt; self&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;http_executor&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;clone&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;();&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;  &#x2F;&#x2F; owned Arc, &amp;#39;static&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;    Box&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;pin&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;async move&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;        do_work&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt;executor)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;await&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;    })&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This is the same idiom Tokio documentation shows for &lt;code&gt;tokio::spawn&lt;&#x2F;code&gt;, which has the same constraint (&lt;code&gt;Send + &#x27;static&lt;&#x2F;code&gt;). The spawn body clones Arcs before &lt;code&gt;move&lt;&#x2F;code&gt; into the task. Trait-level dyn-safe async works the same way for the same reason.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;4-3-if-you-want-to-borrow-self-say-so&quot;&gt;4.3 If you want to borrow &lt;code&gt;self&lt;&#x2F;code&gt;, say so&lt;&#x2F;h3&gt;
&lt;p&gt;You can write a dyn-safe async trait method that &lt;em&gt;does&lt;&#x2F;em&gt; borrow &lt;code&gt;self&lt;&#x2F;code&gt;, by adding the lifetime:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;fn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; execute_prepared&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;a&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;    &amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;a&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language z-self&quot;&gt; self&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;    ...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Pin&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;Box&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;dyn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Future&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;Output&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; T&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; +&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Send&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; +&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;a&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;gt;&amp;gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now the future is permitted to borrow &lt;code&gt;self&lt;&#x2F;code&gt; for the duration &lt;code&gt;&#x27;a&lt;&#x2F;code&gt;. The cost is that callers cannot store the future beyond the borrow; it cannot be &lt;code&gt;tokio::spawn&lt;&#x2F;code&gt;ed as-is without some form of owned-handle wrapping. Most service-oriented traits choose the &lt;code&gt;&#x27;static&lt;&#x2F;code&gt; flavor (no &lt;code&gt;&#x27;a&lt;&#x2F;code&gt;) precisely so the futures are spawnable; they accept the clone-before-move cost as the price of spawn-ability.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;5-async-trait-is-the-same-thing-macroed&quot;&gt;5. &lt;code&gt;#[async_trait]&lt;&#x2F;code&gt; is the same thing, macroed&lt;&#x2F;h2&gt;
&lt;p&gt;The &lt;code&gt;async-trait&lt;&#x2F;code&gt; crate (proc macro) lets you write the sugared form:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;#[&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-attribute z-rust&quot;&gt;async_trait&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;pub&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt; trait&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; StepExecutor&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Send&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; +&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Sync&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    async fn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; execute_prepared&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;        &amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language z-self&quot;&gt;self&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;        requests&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Vec&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;PreparedRequest&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;        max_concurrency&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; usize&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;    )&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Vec&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;Result&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;ExecutionResult&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;gt;&amp;gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;At expansion time, the macro rewrites both the trait and every &lt;code&gt;#[async_trait] impl&lt;&#x2F;code&gt; into the manual form we just showed. The return type becomes &lt;code&gt;Pin&amp;lt;Box&amp;lt;dyn Future&amp;lt;Output = ...&amp;gt; + Send + &#x27;async_trait&amp;gt;&amp;gt;&lt;&#x2F;code&gt;, where &lt;code&gt;&#x27;async_trait&lt;&#x2F;code&gt; is a generated lifetime bound to &lt;code&gt;&amp;amp;self&lt;&#x2F;code&gt;. That is the §4.3 borrowing form, &lt;strong&gt;not&lt;&#x2F;strong&gt; the &lt;code&gt;&#x27;static&lt;&#x2F;code&gt; form from §3 - the macro lets the future borrow &lt;code&gt;self&lt;&#x2F;code&gt; for the duration of the call, so impls can write &lt;code&gt;self.field.do_work().await&lt;&#x2F;code&gt; without cloning. By default the macro adds &lt;code&gt;+ Send&lt;&#x2F;code&gt;; opt out with &lt;code&gt;#[async_trait(?Send)]&lt;&#x2F;code&gt; for futures that don&#x27;t cross thread boundaries.&lt;&#x2F;p&gt;
&lt;p&gt;That is:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;#[async_trait]&lt;&#x2F;code&gt; style and the §4.3 manual form produce &lt;strong&gt;equivalent&lt;&#x2F;strong&gt; desugaring; the macro is a purely syntactic shortcut and buys you nothing at runtime.&lt;&#x2F;li&gt;
&lt;li&gt;It is &lt;strong&gt;not&lt;&#x2F;strong&gt; equivalent to the §3 &lt;code&gt;&#x27;static&lt;&#x2F;code&gt; form. If you want a spawnable, owns-its-data future, the macro alone won&#x27;t give you that - you still write the clone-before-&lt;code&gt;async move&lt;&#x2F;code&gt; dance inside the impl, just without the outer &lt;code&gt;Pin&amp;lt;Box&amp;lt;…&amp;gt;&amp;gt;&lt;&#x2F;code&gt; ceremony.&lt;&#x2F;li&gt;
&lt;li&gt;The cost of the macro is an extra dependency, some compile-time overhead, and a layer of obfuscation when reading expanded diagnostics.&lt;&#x2F;li&gt;
&lt;li&gt;The cost of the manual form is ~5 more lines per method: one &lt;code&gt;fn&lt;&#x2F;code&gt; signature with &lt;code&gt;Pin&amp;lt;Box&amp;lt;…&amp;gt;&amp;gt;&lt;&#x2F;code&gt;, one &lt;code&gt;Box::pin(async move { … })&lt;&#x2F;code&gt;, one set of field clones.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Choosing between them is largely a judgment call about dependency hygiene and explicitness. For a library with a handful of dyn-safe async methods on one trait, the manual form is often preferable: no proc macro in the dep graph, the reader sees exactly what&#x27;s happening, and rustc diagnostics are about your real code rather than generated code.&lt;&#x2F;p&gt;
&lt;p&gt;For a codebase with dozens of such traits, &lt;code&gt;#[async_trait]&lt;&#x2F;code&gt; amortizes its dep cost and reduces ceremony. Common Rust advice: prefer the manual form when it&#x27;s small and contained; prefer &lt;code&gt;#[async_trait]&lt;&#x2F;code&gt; when the volume justifies it.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;6-alternatives-and-when-to-reach-for-them&quot;&gt;6. Alternatives and when to reach for them&lt;&#x2F;h2&gt;
&lt;p&gt;The boxed form is not the only way to make async work across trait implementations. It is the most common one when you need &lt;code&gt;dyn Trait&lt;&#x2F;code&gt;. Here are the alternatives worth knowing.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;6-1-associated-future-type-the-tower-style&quot;&gt;6.1 Associated future type (the &quot;tower&quot; style)&lt;&#x2F;h3&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;pub&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt; trait&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Service&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;    type&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Response&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;    type&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Error&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;    type&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Future&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Future&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;Output&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Result&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language z-self&quot;&gt;Self&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;Response&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language z-self&quot;&gt; Self&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;Error&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; +&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Send&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    fn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; call&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-storage&quot;&gt;&amp;amp;mut&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language z-self&quot;&gt; self&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-variable&quot;&gt;, req&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Request&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language z-self&quot;&gt; Self&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;Future&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Each implementation names its own future type. No boxing, no allocation per call. The catch: it is not dyn-compatible on its own - to use &lt;code&gt;dyn Service&lt;&#x2F;code&gt;, you typically wrap it with a helper like &lt;code&gt;tower::util::BoxService&lt;&#x2F;code&gt; that boxes the future at the object boundary. &lt;code&gt;tower&lt;&#x2F;code&gt; takes this approach precisely to decouple the trait&#x27;s abstract shape (zero-cost associated future) from the erasure-at-the-edge concern (box only when you need dyn).&lt;&#x2F;p&gt;
&lt;p&gt;Good choice when you are writing a service that will &lt;em&gt;usually&lt;&#x2F;em&gt; be composed generically and &lt;em&gt;occasionally&lt;&#x2F;em&gt; boxed. Overkill for traits that always want dyn dispatch anyway.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;6-2-native-async-fn-in-traits-without-dyn&quot;&gt;6.2 Native &lt;code&gt;async fn&lt;&#x2F;code&gt; in traits (without &lt;code&gt;dyn&lt;&#x2F;code&gt;)&lt;&#x2F;h3&gt;
&lt;p&gt;If you do not need &lt;code&gt;dyn Trait&lt;&#x2F;code&gt;, post-1.75 you can simply write:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;pub&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt; trait&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; StepExecutor&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Send&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; +&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Sync&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    async fn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; execute_prepared&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language z-self&quot;&gt;self&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-variable&quot;&gt;, requests&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Vec&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;PreparedRequest&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Vec&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;Result&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;()&amp;gt;&amp;gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Callers use generics: &lt;code&gt;fn run&amp;lt;E: StepExecutor&amp;gt;(e: E)&lt;&#x2F;code&gt; rather than &lt;code&gt;fn run(e: Box&amp;lt;dyn StepExecutor&amp;gt;)&lt;&#x2F;code&gt;. The compiler monomorphizes, inlines, and produces tight code with no boxed futures.&lt;&#x2F;p&gt;
&lt;p&gt;Downside: you lose type erasure. Everywhere &lt;code&gt;StepExecutor&lt;&#x2F;code&gt; appears in a signature, the concrete impl leaks through as a generic parameter. If you were using &lt;code&gt;dyn&lt;&#x2F;code&gt; specifically to hide the impl from downstream code (test vs production, feature-gated variants), that hiding is gone.&lt;&#x2F;p&gt;
&lt;p&gt;There is a second, more subtle downside - the &lt;strong&gt;Send bound problem&lt;&#x2F;strong&gt;. With native &lt;code&gt;async fn&lt;&#x2F;code&gt; in traits, you cannot directly write &quot;the returned future must be &lt;code&gt;Send&lt;&#x2F;code&gt;&quot; in the trait declaration. The desugared &lt;code&gt;impl Future&lt;&#x2F;code&gt; is opaque; there is no straightforward syntax to constrain its auto-traits at the trait-definition site. A consumer that needs to &lt;code&gt;tokio::spawn&lt;&#x2F;code&gt; the future therefore has no way to demand &lt;code&gt;Send&lt;&#x2F;code&gt; from the trait alone. This is exactly why many codebases on Rust 1.75+ still reach for &lt;code&gt;#[async_trait]&lt;&#x2F;code&gt; even when they use generics: the macro&#x27;s expansion to &lt;code&gt;Pin&amp;lt;Box&amp;lt;dyn Future + Send + &#x27;async_trait&amp;gt;&amp;gt;&lt;&#x2F;code&gt; makes &lt;code&gt;Send&lt;&#x2F;code&gt; a contractual part of the trait signature.&lt;&#x2F;p&gt;
&lt;p&gt;The language has partial answers - &lt;strong&gt;return-type notation&lt;&#x2F;strong&gt; (&lt;code&gt;where T::method(..): Send&lt;&#x2F;code&gt;, still being stabilized) lets a caller demand &lt;code&gt;Send&lt;&#x2F;code&gt; on a per-method basis at the use site, and the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.rs&#x2F;trait-variant&quot;&gt;&lt;code&gt;trait-variant&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; crate generates a parallel &lt;code&gt;Send&lt;&#x2F;code&gt;-flavored trait from a single declaration - but neither is as ergonomic as the boxed form for the &quot;every future must be &lt;code&gt;Send&lt;&#x2F;code&gt;&quot; case. If your trait is intended for spawning, the boxed &lt;code&gt;dyn&lt;&#x2F;code&gt; form remains the path of least resistance.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;6-3-both-via-a-helper-boxed-t-wrapper&quot;&gt;6.3 Both, via a helper &lt;code&gt;Boxed&amp;lt;T&amp;gt;&lt;&#x2F;code&gt; wrapper&lt;&#x2F;h3&gt;
&lt;p&gt;The pattern &lt;code&gt;tower::util::BoxService&lt;&#x2F;code&gt; uses: a thin wrapper that implements the trait by boxing the associated future at each call. You define the trait with the native&#x2F;associated form, then provide a &lt;code&gt;Boxed&lt;&#x2F;code&gt; adapter for the erasure case.&lt;&#x2F;p&gt;
&lt;p&gt;More code to maintain, but you get both generic and dyn dispatch from the same abstraction.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;6-4-returning-impl-future-directly-no-dyn-no-macro&quot;&gt;6.4 Returning &lt;code&gt;impl Future&lt;&#x2F;code&gt; directly (no dyn, no macro)&lt;&#x2F;h3&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;pub&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt; trait&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; StepExecutor&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    fn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; execute_prepared&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language z-self&quot;&gt;self&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-variable&quot;&gt;, requests&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Vec&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;PreparedRequest&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;        -&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; impl&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Future&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;Output&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Vec&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;Result&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;()&amp;gt;&amp;gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; +&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Send&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Available since 1.75. Equivalent to &lt;code&gt;async fn&lt;&#x2F;code&gt; in a trait. Same dyn-incompatibility. Same monomorphization behavior.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;7-how-to-read-a-method-written-in-the-manual-form&quot;&gt;7. How to read a method written in the manual form&lt;&#x2F;h2&gt;
&lt;p&gt;When you encounter code like this in a codebase:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;fn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; do_thing&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;    &amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language z-self&quot;&gt;self&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    arg&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; SomeArg&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Pin&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;Box&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;dyn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Future&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;Output&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Result&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;Thing&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; +&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Send&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;gt;&amp;gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;    let&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; state&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language z-self&quot;&gt; self&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;state&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;clone&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;();&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;    let&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; client&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language z-self&quot;&gt; self&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;client&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;clone&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;();&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;    Box&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;pin&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;async move&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;        let&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; s&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; state&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;lock&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;()&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;();&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;        client&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;request&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-variable&quot;&gt;(s&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;derive_key&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-punctuation&quot;&gt;arg))&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;await&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;    })&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Read it as:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Signature&lt;&#x2F;strong&gt;: &quot;This is &lt;code&gt;async fn do_thing(&amp;amp;self, arg: SomeArg) -&amp;gt; Result&amp;lt;Thing&amp;gt;&lt;&#x2F;code&gt;, written in the dyn-compatible desugared form. The returned future is &lt;code&gt;Send + &#x27;static&lt;&#x2F;code&gt; - spawnable, cross-thread-safe, owns its data.&quot;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;The field clones&lt;&#x2F;strong&gt;: &quot;These are the captures the future needs. They are cloned because the future is &lt;code&gt;&#x27;static&lt;&#x2F;code&gt; and cannot borrow &lt;code&gt;self&lt;&#x2F;code&gt;.&quot;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;Box::pin(async move { ... })&lt;&#x2F;code&gt;&lt;&#x2F;strong&gt;: &quot;This is the function body of the &lt;code&gt;async fn&lt;&#x2F;code&gt;. It allocates one pinned future on the heap and erases its concrete type.&quot;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;The async block&#x27;s contents&lt;&#x2F;strong&gt;: the real logic.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Once you have done this translation a few times, the extra ceremony recedes into background pattern. The first time, it looks like ritual; the second time, it looks like the compiler speaking through the code.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;8-when-the-pattern-is-right-and-when-it-is-not&quot;&gt;8. When the pattern is right and when it is not&lt;&#x2F;h2&gt;
&lt;p&gt;Use the manual &lt;code&gt;Pin&amp;lt;Box&amp;lt;dyn Future + Send&amp;gt;&amp;gt;&lt;&#x2F;code&gt; form when:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;You need a trait object (&lt;code&gt;Box&amp;lt;dyn Trait&amp;gt;&lt;&#x2F;code&gt;, &lt;code&gt;&amp;amp;dyn Trait&lt;&#x2F;code&gt;, &lt;code&gt;Arc&amp;lt;dyn Trait&amp;gt;&lt;&#x2F;code&gt;) with async methods.&lt;&#x2F;li&gt;
&lt;li&gt;You want to avoid the &lt;code&gt;async-trait&lt;&#x2F;code&gt; proc-macro dependency for a small number of methods.&lt;&#x2F;li&gt;
&lt;li&gt;You want the desugaring explicit in the source so readers can inspect the lifetime and &lt;code&gt;Send&lt;&#x2F;code&gt; bounds directly.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Use &lt;code&gt;#[async_trait]&lt;&#x2F;code&gt; when:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;You have many async trait methods and the repetition overhead is real.&lt;&#x2F;li&gt;
&lt;li&gt;You want the sugared &lt;code&gt;async fn&lt;&#x2F;code&gt; reading experience across a team that&#x27;s less practiced in manual futures.&lt;&#x2F;li&gt;
&lt;li&gt;You don&#x27;t mind one proc-macro dep.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Skip the dyn form entirely (use generics + &lt;code&gt;async fn in traits&lt;&#x2F;code&gt;) when:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;You never need &lt;code&gt;dyn Trait&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;All your trait consumers are generic code that monomorphizes.&lt;&#x2F;li&gt;
&lt;li&gt;You want peak performance - no per-call boxing, no vtable indirection.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Use associated futures (&lt;code&gt;type Future: Future&amp;lt;Output = …&amp;gt;;&lt;&#x2F;code&gt;) when:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;You are writing a library that wants to support both generic and boxed use without committing to one, at the cost of more scaffolding.&lt;&#x2F;li&gt;
&lt;li&gt;You have performance-critical hot paths where the per-call &lt;code&gt;Box::pin&lt;&#x2F;code&gt; allocation measurably matters.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;9-quick-reference&quot;&gt;9. Quick reference&lt;&#x2F;h2&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;&#x2F;&#x2F; Dyn-safe async method, manual desugar:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;fn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; foo&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language z-self&quot;&gt;self&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-variable&quot;&gt;, x&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; T&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Pin&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;Box&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;dyn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Future&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;Output&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; R&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; +&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Send&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;gt;&amp;gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;    let&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; state&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language z-self&quot;&gt; self&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;state&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;clone&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;();&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;       &#x2F;&#x2F; clone captures (no &amp;amp;self in body)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;    Box&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;pin&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;async move&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;        state&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;do_work&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-variable&quot;&gt;(x)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;await&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;    })&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;&#x2F;&#x2F; Dyn-safe async method, borrowing self:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;fn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; foo&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;a&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;a&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language z-self&quot;&gt; self&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-variable&quot;&gt;, x&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; T&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Pin&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;Box&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;dyn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Future&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;Output&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; R&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; +&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Send&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; +&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;a&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;gt;&amp;gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;    Box&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;pin&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;async move&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-language z-self&quot;&gt;        self&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function&quot;&gt;state&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt;do_work&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-variable&quot;&gt;(x)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;await&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;         &#x2F;&#x2F; can touch self; future tied to &amp;#39;a&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;    })&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;&#x2F;&#x2F; #[async_trait] equivalent - compiler expands to the dyn-safe form:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;#[&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-attribute z-rust&quot;&gt;async_trait&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;trait&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Foo&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    async fn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; foo&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language z-self&quot;&gt;self&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-variable&quot;&gt;, x&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; T&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; R&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;&#x2F;&#x2F; Native async fn in trait (Rust 1.75+) - works for generics, NOT for dyn:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;trait&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Foo&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    async fn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; foo&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language z-self&quot;&gt;self&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-variable&quot;&gt;, x&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; T&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; R&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;&#x2F;&#x2F; Tower-style associated future - no box by default:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;trait&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Foo&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;    type&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Future&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Future&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;Output&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; R&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; +&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; Send&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    fn&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function&quot;&gt; foo&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language z-self&quot;&gt;self&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-variable&quot;&gt;, x&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt; T&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language z-self&quot;&gt; Self&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-type&quot;&gt;Future&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;10-takeaway&quot;&gt;10. Takeaway&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;code&gt;async fn&lt;&#x2F;code&gt; in traits and &lt;code&gt;Box&amp;lt;dyn Trait&amp;gt;&lt;&#x2F;code&gt; are two features that Rust individually supports and jointly conflict. Every implementation of an async trait method produces a different anonymous future type; vtables cannot dispatch over anonymous types; therefore dyn-safe async methods must return a concrete, type-erased representation of a future. The only such representation the language offers is &lt;code&gt;Pin&amp;lt;Box&amp;lt;dyn Future&amp;lt;Output = T&amp;gt; + Send&amp;gt;&amp;gt;&lt;&#x2F;code&gt; - a pinned, boxed, erased trait object of &lt;code&gt;Future&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Everything else in the manual form is a consequence of that single decision:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;Box::pin(async move { ... })&lt;&#x2F;code&gt; wrap produces the erased value.&lt;&#x2F;li&gt;
&lt;li&gt;The clone-before-move on &lt;code&gt;Arc&lt;&#x2F;code&gt;-wrapped fields is because the future must be &lt;code&gt;&#x27;static&lt;&#x2F;code&gt; (no lifetime parameter on the return type) and therefore cannot capture &lt;code&gt;&amp;amp;self&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;The &lt;code&gt;#[async_trait]&lt;&#x2F;code&gt; macro expands to exactly this shape; reading it as generated code makes the behavior predictable.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Once the return type is recognized for what it is - the compiler&#x27;s recommended lowering for &quot;async in a dyn trait&quot; - the rest of the ceremony reads as mechanics, not cleverness.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;references&quot;&gt;References&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;crates&quot;&gt;Crates&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.rs&#x2F;tower&quot;&gt;&lt;code&gt;tower&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; (&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tower-rs&#x2F;tower&quot;&gt;repo&lt;&#x2F;a&gt;) - modular service&#x2F;middleware abstraction built around the &lt;code&gt;Service&lt;&#x2F;code&gt; trait with an associated future type. Source of the &quot;tower style&quot; in §6.1.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.rs&#x2F;tower&#x2F;latest&#x2F;tower&#x2F;util&#x2F;struct.BoxService.html&quot;&gt;&lt;code&gt;tower::util::BoxService&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; - the adapter that boxes the future at the dyn boundary, so a tower service can be used behind &lt;code&gt;dyn&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.rs&#x2F;async-trait&quot;&gt;&lt;code&gt;async-trait&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; (&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;dtolnay&#x2F;async-trait&quot;&gt;repo&lt;&#x2F;a&gt;) - the proc macro that expands sugared &lt;code&gt;async fn&lt;&#x2F;code&gt; in traits into the manual &lt;code&gt;Pin&amp;lt;Box&amp;lt;dyn Future + Send&amp;gt;&amp;gt;&lt;&#x2F;code&gt; form.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;hyper.rs&#x2F;&quot;&gt;&lt;code&gt;hyper&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;hyperium&#x2F;tonic&quot;&gt;&lt;code&gt;tonic&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; - HTTP and gRPC libraries built on top of &lt;code&gt;tower::Service&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;language-and-standard-library&quot;&gt;Language and standard library&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;blog.rust-lang.org&#x2F;2023&#x2F;12&#x2F;28&#x2F;Rust-1.75.0.html&quot;&gt;Rust 1.75 release notes&lt;&#x2F;a&gt; - the release that stabilized &lt;code&gt;async fn&lt;&#x2F;code&gt; in traits and &lt;code&gt;-&amp;gt; impl Trait&lt;&#x2F;code&gt; in trait methods (RPITIT).&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;blog.rust-lang.org&#x2F;inside-rust&#x2F;2023&#x2F;05&#x2F;03&#x2F;stabilizing-async-fn-in-trait.html&quot;&gt;Announcing &lt;code&gt;async fn&lt;&#x2F;code&gt; and return-position &lt;code&gt;impl Trait&lt;&#x2F;code&gt; in traits&lt;&#x2F;a&gt; - the lang team post explaining the feature, its limitations, and explicitly why it does not work with &lt;code&gt;dyn Trait&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;error_codes&#x2F;E0038.html&quot;&gt;E0038: the trait is not dyn compatible&lt;&#x2F;a&gt; - the error code emitted when a trait cannot be made into a trait object, with the full list of reasons.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;reference&#x2F;items&#x2F;traits.html#object-safety&quot;&gt;Reference: object safety &#x2F; dyn compatibility&lt;&#x2F;a&gt; - the language reference&#x27;s definition of which traits can be used as &lt;code&gt;dyn Trait&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;std&#x2F;pin&#x2F;&quot;&gt;&lt;code&gt;std::pin&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; - module documentation for &lt;code&gt;Pin&lt;&#x2F;code&gt;, including why self-referential futures need pinning.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;std&#x2F;future&#x2F;trait.Future.html&quot;&gt;&lt;code&gt;std::future::Future&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; - the trait being boxed.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;background-reading&quot;&gt;Background reading&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;rust-lang.github.io&#x2F;async-book&#x2F;&quot;&gt;The Async Rust Book&lt;&#x2F;a&gt; - foundational material on &lt;code&gt;Future&lt;&#x2F;code&gt;, &lt;code&gt;async&lt;&#x2F;code&gt;&#x2F;&lt;code&gt;await&lt;&#x2F;code&gt;, executors, and pinning.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;tokio.rs&#x2F;tokio&#x2F;tutorial&#x2F;spawning&quot;&gt;Tokio tutorial: Spawning&lt;&#x2F;a&gt; - explains the &lt;code&gt;Send + &#x27;static&lt;&#x2F;code&gt; requirement for &lt;code&gt;tokio::spawn&lt;&#x2F;code&gt;, which is the same constraint that drives the clone-before-&lt;code&gt;async move&lt;&#x2F;code&gt; idiom in §4.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</description>
      </item>
    </channel>
</rss>
