<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
    <title>Ruminations of a Programmer</title>
    <subtitle>Programming, some math and technical deep dives</subtitle>
    <link rel="self" type="application/atom+xml" href="https://debasishg.github.io/atom.xml"/>
    <link rel="alternate" type="text/html" href="https://debasishg.github.io"/>
    <generator uri="https://www.getzola.org/">Zola</generator>
    <updated>2026-05-04T00:00:00+00:00</updated>
    <id>https://debasishg.github.io/atom.xml</id>
    <entry xml:lang="en">
        <title>Evaluating PBT Frameworks: How Proptest and Hegel Differ in Algebraic Expressivity</title>
        <published>2026-05-04T00:00:00+00:00</published>
        <updated>2026-05-04T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://debasishg.github.io/blog/proptest-hegel/"/>
        <id>https://debasishg.github.io/blog/proptest-hegel/</id>
        
        <content type="html" 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;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>The RAII Drop-Guard Pattern in Rust</title>
        <published>2026-04-30T00:00:00+00:00</published>
        <updated>2026-04-30T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://debasishg.github.io/blog/raii-guard/"/>
        <id>https://debasishg.github.io/blog/raii-guard/</id>
        
        <content type="html" 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;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Dyn-Compatible Async Traits in Rust: Why the Manual Boxed Future Idiom is Required</title>
        <published>2026-04-26T00:00:00+00:00</published>
        <updated>2026-04-26T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://debasishg.github.io/blog/pin-dyn/"/>
        <id>https://debasishg.github.io/blog/pin-dyn/</id>
        
        <content type="html" 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;
</content>
        
    </entry>
</feed>
