<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom"><link href="https://theincredibleholk.org/atom.xml" rel="self" type="application/atom+xml"/><link href="https://theincredibleholk.org" rel="alternate" type="text/html"/><updated>2026-01-02T21:43:35.625538838+00:00</updated><id>https://theincredibleholk.org/atom.xml</id><title type="html">Eric Holk</title><subtitle>Articles about coding, making, and life.</subtitle><author><name>Eric Holk</name></author><entry><title type="html">Musings on the AI Apocalypse</title><link href="https://theincredibleholk.org/blog/2025/12/26/musings-on-the-ai-apocalypse/" rel="alternate" type="text/html" title="Eric Holk"/><published>2025-12-26T00:00:00+00:00</published><updated>2025-12-26T00:00:00+00:00</updated><id>https://theincredibleholk.org/blog/2025/12/26/musings-on-the-ai-apocalypse/</id><content type="html" xml:base="https://theincredibleholk.org/blog/2025/12/26/musings-on-the-ai-apocalypse/"><![CDATA[<p>Around March of this year, my wife and I were driving somewhere.
I turned to her and said, "What should we do if in two to five years all of my skills are economically worthless?"</p>
<!-- MORE -->
<p>Looking back on 2025, I'm realizing this may turn out to have been a transformational year.
Of course, it's also likely that I will look back on this post in ten years and think how naive and foolish I was.
So far though, the signs look to me to be on the side of radical transformation.</p>
<p>For me there were two things that happened around March that shook my worldview and led to that conversation with my wife.</p>
<p>First, I tried Github Copilot's agent mode.</p>
<p>My experience with vibe coding to that point had been mostly "this is pretty neat, but it is fundamentally limited."
But I also noticed that if you ask an LLM to write some code, compile it, and paste the error messages into the chat, the LLM could often fix them.
It makes sense. Most of my code doesn't work the first time either, but by iterating with the tools at my disposal I can usually get something working.
Agent mode automated this copy-paste loop and made it feel like a bot that could complete well-defined units of work on its own.
That was the moment that made me think there might really be something here.</p>
<p>Agent mode demonstrates something else that's powerful: it creates an environment where self-play is possible.
The last few years' progress in AI has mostly come from training on larger and larger data sets.
By now, we have trained on essentially the entire intellectual output of the human race.
There just doesn't seem to be enough data to keep making gains in the same way.
On the other hand, AlphaGo and friends have shown that self-play can work wildly well, so why wouldn't we expect the same if we could apply self-play to LLMs?
Coding is basically a game we play against the compiler and the laws of mathematics.
It's easy to tell if we've won, because our code compiles, the tests pass, etc.
This means AI can create its own training data when working on coding problems by training on traces that led to successfully completing the task.<sup class="footnote-reference"><a href="#rlvr" id="fnref:rlvr">1</a></sup></p>
<p>Second, I read <a href="https://ai-2027.com/">AI 2027</a> <a href="https://web.archive.org/web/20251228195457/https://ai-2027.com/" title="View archived version from 28 December 2025"><span class="wayback-indicator"></span></a>.</p>
<p>I know the main authors more on the alarmist end of the spectrum, but I think AI 2027 made such a big impact on me because I read it on the heels of being astounded by agent mode.
I'll admit, the idea that we'll have superintelligent nanobots in just two years sounds absurd.
On the other hand, the individual steps they laid out from here to there do not strike me as beyond the realm of plausibility.
One of the things that was really powerful about the AI 2027 scenario was that the timelines are steep enough that months matter.
Things happen on a timescale that we can see play out in essentially real time.
Even if 2027 is too early, 2030 or 2035 or 2040 would still impact my life in profound ways.</p>
<p>With that in mind, Al 2027 gives me a baseline to evaluate subsequent developments against.
It gives me a way to say" are we ahead of schedule or behind?"</p>
<p>We're only about six months out so maybe we shouldn't expect much progress along that scenario.
On the other hand, that's about 20% of the whole 2027 timeline, so it seems like a reasonable time to start evaluating some of their predictions.
On the quantitative side, my sense is we are behind their compute projections, but perhaps making better than expected algorithmic progress.
The qualitative experience seems pretty spot on.
We have agents that are able to do surprising things but fail in hilarious and sometimes adorable ways.
We're also starting to see AI become a significant political issue.
All in all I'd say the overall 2027 scenario does not yet seem fundamentally broken.</p>
<p>This past year I've found myself reflecting often on a conversation I had around 2005.
I was in college working on a computer science degree, talking with a friend who worked in an auto parts factory. I said something about how the Internet had completely transformed our way of life.
My friend responded, "Maybe for you, but it hasn't made much difference to normal people's lives!"
At the time it was a good reminder that I lived in a techno-bubble and that my experiences maybe were not typical.
But fast forward 20 years and the internet is everywhere.
We do so much shopping online that it's getting harder to even find brick-and-mortor stores.<sup class="footnote-reference"><a href="#silicon-valley" id="fnref:silicon-valley">2</a></sup>
We organize social groups through things like Facebook and WhatsApp.
We RSVP for weddings online and plan baby showers online.
The Internet has been blamed for the downfall of democracy, crippling societal anxiety, and any number of nation- and world-wide issues.
I think it's fair to say the Internet has transformed the lives even of non-computer nerds.</p>
<p>I have a feeling now is a similar time with AI.
Those of us in the tech industry are positioned to see the strongest early impacts, while for most people AI has not lived up to the hype.<sup class="footnote-reference"><a href="#hype" id="fnref:hype">3</a></sup>
But I doubt that coding will be AI's last success.</p>
<p>So back to that car ride.
My wife asked if I really thought this would happen, that in a few years computers would be able to do all the work I do now.
"Probably not," I said, "but it seems like enough of a possibility that it's worth having a plan."</p>
<hr />
<div class="footnote-definition" id="rlvr"><sup class="footnote-definition-label">1</sup>
<p>I've since learned this is called Reinforcement Learning with Verifiable Rewards (RLVR), and it seems it has provided a lot of the coding progress for recent models.<a href="#fnref:rlvr" class="footnote-backref">↩</a></p>
</div>
<div class="footnote-definition" id="silicon-valley"><sup class="footnote-definition-label">2</sup>
<p>That's my experience, anyway, but I also live in Silicon Valley. Maybe in the rest of the world you can still drive to stores and shop in person.<a href="#fnref:silicon-valley" class="footnote-backref">↩</a></p>
</div>
<div class="footnote-definition" id="hype"><sup class="footnote-definition-label">3</sup>
<p>To be fair, it hasn't lived up to the hype for coding agents either, but I think that has more to do with how much hype there is. The coding capabilities of current models genuinely impress me, despite the shortcomings they still have.<a href="#fnref:hype" class="footnote-backref">↩</a></p>
</div>
]]></content><author><name>Eric Holk</name></author><summary type="html"><![CDATA[<p>Around March of this year, my wife and I were driving somewhere.
I turned to her and said, "What should we do if in two to five years all of my skills are economically worthless?"</p>
]]></summary></entry><entry><title type="html">Anonymous Impls</title><link href="https://theincredibleholk.org/blog/2024/08/26/anonymous-impls/" rel="alternate" type="text/html" title="Eric Holk"/><published>2024-08-26T00:00:00+00:00</published><updated>2024-08-26T00:00:00+00:00</updated><id>https://theincredibleholk.org/blog/2024/08/26/anonymous-impls/</id><content type="html" xml:base="https://theincredibleholk.org/blog/2024/08/26/anonymous-impls/"><![CDATA[<p>I often find myself writing a function that internally defines a struct just so I can implement a trait on it and return that type as an <code>impl Trait</code>.
To see what I mean, let's look at a concrete example.
We'll write a function that takes an iterator that yields, for example, 1, 2, 3, 4 and returns a new iterator that yields (1, 2), (3, 4).</p>
<pre style="background-color:#ffffff;">
<span style="font-style:italic;color:#969896;">/// Assumes the input iterator yields an even number of elements.
</span><span style="font-weight:bold;color:#a71d5d;">fn </span><span style="font-weight:bold;color:#795da3;">adjacent_pairs</span><span style="color:#323232;">&lt;I: Iterator&gt;(xs: I) -&gt; impl Iterator&lt;Item = </span><span style="font-weight:bold;color:#a71d5d;">I::</span><span style="color:#323232;">Item&gt; {
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">struct </span><span style="color:#323232;">Pairs&lt;I: Iterator&gt; {
</span><span style="color:#323232;">        xs: I,
</span><span style="color:#323232;">    }
</span><span style="color:#323232;">    
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">impl</span><span style="color:#323232;">&lt;I: Iterator&gt; Iterator </span><span style="font-weight:bold;color:#a71d5d;">for </span><span style="color:#323232;">Pairs&lt;I&gt; {
</span><span style="color:#323232;">        </span><span style="font-weight:bold;color:#a71d5d;">type </span><span style="color:#323232;">Item </span><span style="font-weight:bold;color:#a71d5d;">= </span><span style="color:#323232;">I::Item;
</span><span style="color:#323232;">        
</span><span style="color:#323232;">        </span><span style="font-weight:bold;color:#a71d5d;">fn </span><span style="font-weight:bold;color:#795da3;">next</span><span style="color:#323232;">(</span><span style="font-weight:bold;color:#a71d5d;">&amp;mut </span><span style="color:#323232;">self) -&gt; Option&lt;</span><span style="font-weight:bold;color:#a71d5d;">Self::</span><span style="color:#323232;">Item&gt; {
</span><span style="color:#323232;">            </span><span style="font-weight:bold;color:#a71d5d;">let</span><span style="color:#323232;"> first </span><span style="font-weight:bold;color:#a71d5d;">= </span><span style="color:#323232;">self.xs.</span><span style="color:#62a35c;">next</span><span style="color:#323232;">()</span><span style="font-weight:bold;color:#a71d5d;">?</span><span style="color:#323232;">;
</span><span style="color:#323232;">            </span><span style="font-weight:bold;color:#a71d5d;">let</span><span style="color:#323232;"> second </span><span style="font-weight:bold;color:#a71d5d;">= </span><span style="color:#323232;">self.xs.</span><span style="color:#62a35c;">next</span><span style="color:#323232;">()
</span><span style="color:#323232;">                .</span><span style="color:#62a35c;">expect</span><span style="color:#323232;">(</span><span style="color:#183691;">&quot;source iterator must have even length&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">            
</span><span style="color:#323232;">            </span><span style="color:#0086b3;">Some</span><span style="color:#323232;">((first, second))
</span><span style="color:#323232;">        }
</span><span style="color:#323232;">        
</span><span style="color:#323232;">        </span><span style="font-weight:bold;color:#a71d5d;">fn </span><span style="font-weight:bold;color:#795da3;">size_hint</span><span style="color:#323232;">(</span><span style="font-weight:bold;color:#a71d5d;">&amp;</span><span style="color:#323232;">self) -&gt; (</span><span style="font-weight:bold;color:#a71d5d;">usize</span><span style="color:#323232;">, Option&lt;</span><span style="font-weight:bold;color:#a71d5d;">usize</span><span style="color:#323232;">&gt;) {
</span><span style="color:#323232;">            </span><span style="font-weight:bold;color:#a71d5d;">let </span><span style="color:#323232;">(size, max) </span><span style="font-weight:bold;color:#a71d5d;">= </span><span style="color:#323232;">self.xs.</span><span style="color:#62a35c;">size_hint</span><span style="color:#323232;">();
</span><span style="color:#323232;">            
</span><span style="color:#323232;">            (size </span><span style="font-weight:bold;color:#a71d5d;">/ </span><span style="color:#0086b3;">2</span><span style="color:#323232;">, max.</span><span style="color:#62a35c;">map</span><span style="color:#323232;">(|max| max </span><span style="font-weight:bold;color:#a71d5d;">/ </span><span style="color:#0086b3;">2</span><span style="color:#323232;">))
</span><span style="color:#323232;">        }
</span><span style="color:#323232;">    }
</span><span style="color:#323232;">    
</span><span style="color:#323232;">    Pairs {
</span><span style="color:#323232;">        xs,
</span><span style="color:#323232;">    }
</span><span style="color:#323232;">}
</span></pre>
<p>There's a mildly annoying amount of ceremony here.
We have to define a struct that we construct exactly once.
We also have to duplicate the type parameters basically everywhere.
It's not the end of the world, but what if we could make this a little shorter?</p>
<p>This is where a potential new Rust feature called <em>anonymous impls</em> could help.</p>
<!-- MORE -->
<h1 id="introducing-anonymous-impls">Introducing Anonymous Impls<a class="header-anchor" href="#introducing-anonymous-impls">🔗</a></h1>
<p>I think the easiest way to introduce anonymous impls is to rewrite our example using them.</p>
<pre style="background-color:#ffffff;">
<span style="font-style:italic;color:#969896;">/// Assumes the input iterator yields an even number of elements.
</span><span style="font-weight:bold;color:#a71d5d;">fn </span><span style="font-weight:bold;color:#795da3;">adjacent_pairs</span><span style="color:#323232;">&lt;I: Iterator&gt;(xs: I) -&gt; impl Iterator&lt;Item = </span><span style="font-weight:bold;color:#a71d5d;">I::</span><span style="color:#323232;">Item&gt; {
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">return impl </span><span style="color:#323232;">Iterator {
</span><span style="color:#323232;">        </span><span style="font-weight:bold;color:#a71d5d;">use</span><span style="color:#323232;"> xs;
</span><span style="color:#323232;">        
</span><span style="color:#323232;">        </span><span style="font-weight:bold;color:#a71d5d;">type </span><span style="color:#323232;">Item </span><span style="font-weight:bold;color:#a71d5d;">= </span><span style="color:#323232;">I::Item;
</span><span style="color:#323232;">        
</span><span style="color:#323232;">        </span><span style="font-weight:bold;color:#a71d5d;">fn </span><span style="font-weight:bold;color:#795da3;">next</span><span style="color:#323232;">(</span><span style="font-weight:bold;color:#a71d5d;">&amp;mut </span><span style="color:#323232;">self) -&gt; Option&lt;</span><span style="font-weight:bold;color:#a71d5d;">Self::</span><span style="color:#323232;">Item&gt; {
</span><span style="color:#323232;">            </span><span style="font-weight:bold;color:#a71d5d;">let</span><span style="color:#323232;"> first </span><span style="font-weight:bold;color:#a71d5d;">= </span><span style="color:#323232;">self.xs.</span><span style="color:#62a35c;">next</span><span style="color:#323232;">()</span><span style="font-weight:bold;color:#a71d5d;">?</span><span style="color:#323232;">;
</span><span style="color:#323232;">            </span><span style="font-weight:bold;color:#a71d5d;">let</span><span style="color:#323232;"> second </span><span style="font-weight:bold;color:#a71d5d;">= </span><span style="color:#323232;">self.xs.</span><span style="color:#62a35c;">next</span><span style="color:#323232;">().</span><span style="color:#62a35c;">expect</span><span style="color:#323232;">(</span><span style="color:#183691;">&quot;source iterator must have even length&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">            
</span><span style="color:#323232;">            </span><span style="color:#0086b3;">Some</span><span style="color:#323232;">((first, second))
</span><span style="color:#323232;">        }
</span><span style="color:#323232;">        
</span><span style="color:#323232;">        </span><span style="font-weight:bold;color:#a71d5d;">fn </span><span style="font-weight:bold;color:#795da3;">size_hint</span><span style="color:#323232;">(</span><span style="font-weight:bold;color:#a71d5d;">&amp;</span><span style="color:#323232;">self) -&gt; (</span><span style="font-weight:bold;color:#a71d5d;">usize</span><span style="color:#323232;">, Option&lt;</span><span style="font-weight:bold;color:#a71d5d;">usize</span><span style="color:#323232;">&gt;) {
</span><span style="color:#323232;">            </span><span style="font-weight:bold;color:#a71d5d;">let </span><span style="color:#323232;">(size, max) </span><span style="font-weight:bold;color:#a71d5d;">= </span><span style="color:#323232;">self.xs.</span><span style="color:#62a35c;">size_hint</span><span style="color:#323232;">();
</span><span style="color:#323232;">            
</span><span style="color:#323232;">            (size </span><span style="font-weight:bold;color:#a71d5d;">/ </span><span style="color:#0086b3;">2</span><span style="color:#323232;">, max.</span><span style="color:#62a35c;">map</span><span style="color:#323232;">(|max| max </span><span style="font-weight:bold;color:#a71d5d;">/ </span><span style="color:#0086b3;">2</span><span style="color:#323232;">))
</span><span style="color:#323232;">        }
</span><span style="color:#323232;">    }
</span><span style="color:#323232;">}
</span></pre>
<p>The new syntax allows for using <code>impl</code> in expression position.<sup class="footnote-reference"><a href="#trailing-expr-ambiguity" id="fnref:trailing-expr-ambiguity">1</a></sup>
The body mostly looks the same, except we've added a <code>use</code> line.
This is my proposed syntax for explicit captures.</p>
<p>You might ask why we need explicit captures.
We probably don't strictly need them, but I think it simplifies some things.
The main thing is that methods can have different "modes," as indicated by their <code>self</code> parameter.
When it comes to captures, it's not clear whether I should just write <code>xs</code>, or if I should write <code>self.xs</code>?
If I just write <code>xs</code> and I'm in the <code>size_hint</code> method, can I mutate it?
Instead, we can think of <code>use</code> as pulling a variable from the environment and making it a field on the anonymous impl's <code>Self</code> type.</p>
<h1 id="future-possibilities">Future Possibilities<a class="header-anchor" href="#future-possibilities">🔗</a></h1>
<p>The obvious extension to what I've shown so far is to support implementing multiple traits at once.
For the most part you'd just write <code>impl Trait1 + Trait2 { ... }</code> and implement the union of their methods.
The challenge comes when each trait has a method by the same name.
I suppose in that case we could prefix the method name with the trait name when implementing them, like <code>fn Trait1::foo()</code> and <code>fn Trait2::foo()</code>.
At any rate, I think it'd be fine to ship anonymous impls with just one trait at a time.</p>
<p>I'm more excited about how anoynomous impls could interact with future features like associated type position impl trait (ATPIT), associated type inference, and generators.
To see what I mean, suppose we had a trait <code>IntoGen</code> like this:</p>
<pre style="background-color:#ffffff;">
<span style="font-weight:bold;color:#a71d5d;">trait </span><span style="color:#323232;">IntoGen {
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">type </span><span style="color:#323232;">Item;
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">type </span><span style="color:#323232;">Generator: Gen; </span><span style="font-style:italic;color:#969896;">// Gen is the trait of generators
</span><span style="color:#323232;">    
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">fn </span><span style="font-weight:bold;color:#795da3;">into_gen</span><span style="color:#323232;">(self) -&gt; </span><span style="font-weight:bold;color:#a71d5d;">Self::</span><span style="color:#323232;">Generator;
</span><span style="color:#323232;">    
</span><span style="color:#323232;">    </span><span style="font-style:italic;color:#969896;">// note we&#39;re putting size_hint here instead of in the `Gen` trait
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">fn </span><span style="font-weight:bold;color:#795da3;">size_hint</span><span style="color:#323232;">(</span><span style="font-weight:bold;color:#a71d5d;">&amp;</span><span style="color:#323232;">self) -&gt; (</span><span style="font-weight:bold;color:#a71d5d;">usize</span><span style="color:#323232;">, Option&lt;</span><span style="font-weight:bold;color:#a71d5d;">usize</span><span style="color:#323232;">&gt;);
</span><span style="color:#323232;">}
</span></pre>
<p>Then we could rewrite our running example like this:</p>
<pre style="background-color:#ffffff;">
<span style="font-style:italic;color:#969896;">/// Assumes the input iterator yields an even number of elements.
</span><span style="font-weight:bold;color:#a71d5d;">fn </span><span style="font-weight:bold;color:#795da3;">adjacent_pairs</span><span style="color:#323232;">&lt;I: IntoGen&gt;(xs: I) -&gt; impl IntoGen&lt;Item = </span><span style="font-weight:bold;color:#a71d5d;">I::</span><span style="color:#323232;">Item&gt; {
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">let</span><span style="color:#323232;"> size_hint </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> xs.</span><span style="color:#62a35c;">size_hint</span><span style="color:#323232;">();
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">let</span><span style="color:#323232;"> xs </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> xs.</span><span style="color:#62a35c;">into_gen</span><span style="color:#323232;">();
</span><span style="color:#323232;">    
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">return impl </span><span style="color:#323232;">IntoGen {
</span><span style="color:#323232;">        </span><span style="font-weight:bold;color:#a71d5d;">use</span><span style="color:#323232;"> xs;
</span><span style="color:#323232;">        </span><span style="font-weight:bold;color:#a71d5d;">use</span><span style="color:#323232;"> size_hint;
</span><span style="color:#323232;">        
</span><span style="color:#323232;">        </span><span style="font-style:italic;color:#969896;">// Item associated type is inferred
</span><span style="color:#323232;">        </span><span style="font-style:italic;color:#969896;">// Generator associated type is inferred
</span><span style="color:#323232;">        
</span><span style="color:#323232;">        gen </span><span style="font-weight:bold;color:#a71d5d;">fn </span><span style="font-weight:bold;color:#795da3;">into_gen</span><span style="color:#323232;">(self) </span><span style="background-color:#f5f5f5;font-weight:bold;color:#b52a1d;">yield</span><span style="color:#323232;"> I::Item {
</span><span style="color:#323232;">            </span><span style="font-weight:bold;color:#a71d5d;">loop </span><span style="color:#323232;">{
</span><span style="color:#323232;">                </span><span style="font-weight:bold;color:#a71d5d;">let</span><span style="color:#323232;"> first </span><span style="font-weight:bold;color:#a71d5d;">= </span><span style="color:#323232;">self.xs.</span><span style="color:#62a35c;">next</span><span style="color:#323232;">()</span><span style="font-weight:bold;color:#a71d5d;">?</span><span style="color:#323232;">;
</span><span style="color:#323232;">                </span><span style="font-weight:bold;color:#a71d5d;">let</span><span style="color:#323232;"> second </span><span style="font-weight:bold;color:#a71d5d;">= </span><span style="color:#323232;">self.xs.</span><span style="color:#62a35c;">next</span><span style="color:#323232;">().</span><span style="color:#62a35c;">expect</span><span style="color:#323232;">(</span><span style="color:#183691;">&quot;source iterator must have even length&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">
</span><span style="color:#323232;">                </span><span style="background-color:#f5f5f5;font-weight:bold;color:#b52a1d;">yield</span><span style="color:#323232;"> (first, second)
</span><span style="color:#323232;">            }
</span><span style="color:#323232;">        }
</span><span style="color:#323232;">        
</span><span style="color:#323232;">        </span><span style="font-weight:bold;color:#a71d5d;">fn </span><span style="font-weight:bold;color:#795da3;">size_hint</span><span style="color:#323232;">(</span><span style="font-weight:bold;color:#a71d5d;">&amp;</span><span style="color:#323232;">self) -&gt; (</span><span style="font-weight:bold;color:#a71d5d;">usize</span><span style="color:#323232;">, Option&lt;</span><span style="font-weight:bold;color:#a71d5d;">usize</span><span style="color:#323232;">&gt;) {
</span><span style="color:#323232;">            </span><span style="font-weight:bold;color:#a71d5d;">let </span><span style="color:#323232;">(size, max) </span><span style="font-weight:bold;color:#a71d5d;">= </span><span style="color:#323232;">self.size_hint;
</span><span style="color:#323232;">            
</span><span style="color:#323232;">            (size </span><span style="font-weight:bold;color:#a71d5d;">/ </span><span style="color:#0086b3;">2</span><span style="color:#323232;">, max.</span><span style="color:#62a35c;">map</span><span style="color:#323232;">(|max| max </span><span style="font-weight:bold;color:#a71d5d;">/ </span><span style="color:#0086b3;">2</span><span style="color:#323232;">))
</span><span style="color:#323232;">        }
</span><span style="color:#323232;">    }
</span><span style="color:#323232;">}
</span></pre>
<p>While I've made a couple of leaps here by combining three new features, hopefully the idea comes across.
The main point is designing the API this way would give us a way to use both <code>gen</code> syntax while also being able to implement other trait methods like <code>size_hint</code>.</p>
<p>I think this example is unlikely to be feasible with the current <code>Iterator</code>/<code>IntoIterator</code> design.
But if we're willing to tolerate a bit of an impedence mismatch between today's iterators and whatever we do with generators then something like this might become a possibility.</p>
<h1 id="conclusion">Conclusion<a class="header-anchor" href="#conclusion">🔗</a></h1>
<p>I've intended this post as a quick sketch of an anonymous impls feature.
If we were to add it, we would get some quality of life improvements immediately.
Going further, it could interact synergistically with other features that are likely coming down the pipe.</p>
<hr />
<div class="footnote-definition" id="trailing-expr-ambiguity"><sup class="footnote-definition-label">1</sup>
<p>I prefixed this by return here because otherwise we wouldn't know whether we should be trying to parse <code>impl Iterator {</code> in item position, as an inherent impl on a type called <code>Iterator</code> or in expression position as an anonymous impl. There may be better ways to resolve this ambiguity though.<a href="#fnref:trailing-expr-ambiguity" class="footnote-backref">↩</a></p>
</div>
]]></content><author><name>Eric Holk</name></author><summary type="html"><![CDATA[<p>I often find myself writing a function that internally defines a struct just so I can implement a trait on it and return that type as an <code>impl Trait</code>.
To see what I mean, let's look at a concrete example.
We'll write a function that takes an iterator that yields, for example, 1, 2, 3, 4 and returns a new iterator that yields (1, 2), (3, 4).</p>
<pre style="background-color:#ffffff;">
<span style="font-style:italic;color:#969896;">/// Assumes the input iterator yields an even number of elements.
</span><span style="font-weight:bold;color:#a71d5d;">fn </span><span style="font-weight:bold;color:#795da3;">adjacent_pairs</span><span style="color:#323232;">&lt;I: Iterator&gt;(xs: I) -&gt; impl Iterator&lt;Item = </span><span style="font-weight:bold;color:#a71d5d;">I::</span><span style="color:#323232;">Item&gt; {
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">struct </span><span style="color:#323232;">Pairs&lt;I: Iterator&gt; {
</span><span style="color:#323232;">        xs: I,
</span><span style="color:#323232;">    }
</span><span style="color:#323232;">    
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">impl</span><span style="color:#323232;">&lt;I: Iterator&gt; Iterator </span><span style="font-weight:bold;color:#a71d5d;">for </span><span style="color:#323232;">Pairs&lt;I&gt; {
</span><span style="color:#323232;">        </span><span style="font-weight:bold;color:#a71d5d;">type </span><span style="color:#323232;">Item </span><span style="font-weight:bold;color:#a71d5d;">= </span><span style="color:#323232;">I::Item;
</span><span style="color:#323232;">        
</span><span style="color:#323232;">        </span><span style="font-weight:bold;color:#a71d5d;">fn </span><span style="font-weight:bold;color:#795da3;">next</span><span style="color:#323232;">(</span><span style="font-weight:bold;color:#a71d5d;">&amp;mut </span><span style="color:#323232;">self) -&gt; Option&lt;</span><span style="font-weight:bold;color:#a71d5d;">Self::</span><span style="color:#323232;">Item&gt; {
</span><span style="color:#323232;">            </span><span style="font-weight:bold;color:#a71d5d;">let</span><span style="color:#323232;"> first </span><span style="font-weight:bold;color:#a71d5d;">= </span><span style="color:#323232;">self.xs.</span><span style="color:#62a35c;">next</span><span style="color:#323232;">()</span><span style="font-weight:bold;color:#a71d5d;">?</span><span style="color:#323232;">;
</span><span style="color:#323232;">            </span><span style="font-weight:bold;color:#a71d5d;">let</span><span style="color:#323232;"> second </span><span style="font-weight:bold;color:#a71d5d;">= </span><span style="color:#323232;">self.xs.</span><span style="color:#62a35c;">next</span><span style="color:#323232;">()
</span><span style="color:#323232;">                .</span><span style="color:#62a35c;">expect</span><span style="color:#323232;">(</span><span style="color:#183691;">&quot;source iterator must have even length&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">            
</span><span style="color:#323232;">            </span><span style="color:#0086b3;">Some</span><span style="color:#323232;">((first, second))
</span><span style="color:#323232;">        }
</span><span style="color:#323232;">        
</span><span style="color:#323232;">        </span><span style="font-weight:bold;color:#a71d5d;">fn </span><span style="font-weight:bold;color:#795da3;">size_hint</span><span style="color:#323232;">(</span><span style="font-weight:bold;color:#a71d5d;">&amp;</span><span style="color:#323232;">self) -&gt; (</span><span style="font-weight:bold;color:#a71d5d;">usize</span><span style="color:#323232;">, Option&lt;</span><span style="font-weight:bold;color:#a71d5d;">usize</span><span style="color:#323232;">&gt;) {
</span><span style="color:#323232;">            </span><span style="font-weight:bold;color:#a71d5d;">let </span><span style="color:#323232;">(size, max) </span><span style="font-weight:bold;color:#a71d5d;">= </span><span style="color:#323232;">self.xs.</span><span style="color:#62a35c;">size_hint</span><span style="color:#323232;">();
</span><span style="color:#323232;">            
</span><span style="color:#323232;">            (size </span><span style="font-weight:bold;color:#a71d5d;">/ </span><span style="color:#0086b3;">2</span><span style="color:#323232;">, max.</span><span style="color:#62a35c;">map</span><span style="color:#323232;">(|max| max </span><span style="font-weight:bold;color:#a71d5d;">/ </span><span style="color:#0086b3;">2</span><span style="color:#323232;">))
</span><span style="color:#323232;">        }
</span><span style="color:#323232;">    }
</span><span style="color:#323232;">    
</span><span style="color:#323232;">    Pairs {
</span><span style="color:#323232;">        xs,
</span><span style="color:#323232;">    }
</span><span style="color:#323232;">}
</span></pre>
<p>There's a mildly annoying amount of ceremony here.
We have to define a struct that we construct exactly once.
We also have to duplicate the type parameters basically everywhere.
It's not the end of the world, but what if we could make this a little shorter?</p>
<p>This is where a potential new Rust feature called <em>anonymous impls</em> could help.</p>
]]></summary></entry><entry><title type="html">Two Ways Not to Move</title><link href="https://theincredibleholk.org/blog/2024/07/15/two-ways-not-to-move/" rel="alternate" type="text/html" title="Eric Holk"/><published>2024-07-15T00:00:00+00:00</published><updated>2024-07-15T00:00:00+00:00</updated><id>https://theincredibleholk.org/blog/2024/07/15/two-ways-not-to-move/</id><content type="html" xml:base="https://theincredibleholk.org/blog/2024/07/15/two-ways-not-to-move/"><![CDATA[<p>Lately I've been talking to a few people about whether it might be possible to replace the <code>Pin</code> wrapper in Rust with a new <code>Move</code> trait.
<code>Pin</code> is one of those things that, while amazing that Rust can express the concept and semantics using just a library type, is not a lot of fun to use.
This creates friction in building out Rust's async features because currently doing so increases the opportunities for users to be exposed to <code>Pin</code>.
Maybe we can just get rid of <code>Pin</code> entirely?</p>
<p>One possible way to do this is with a trait called <code>Move</code>.
This would be a trait that most types implement and indicates that you have the ability to perform a move operation on a value of that type.
It's sort of like the <code>Send</code> trait, where when a type implements <code>Send</code> it means values of those types can be sent to another thread.</p>
<p>This trait was considered somewhere in the Rust 2015 and 2018 timeframe but was rejected in favor of <code>Pin</code> instead.
The main reason is that <code>Pin</code> could be added in a way that meets Rust's stability guarantees.
Indeed, how to add <code>Move</code> now is a huge problem that we're going to entirely ignore in this post.</p>
<p>I recently discovered that the version of <code>Move</code> that was originally considered worked quite different from how I would have expected.
As a result, we accidentally have two <code>Move</code> designs.
In this post, I want to briefly describe each one and then discuss some of the tradeoffs they make.</p>
<!-- MORE -->
<h1 id="the-original-move">The Original <code>Move</code><a class="header-anchor" href="#the-original-move">🔗</a></h1>
<p>Originally <code>Move</code> was a marker trait that meant values of a type that implements <code>Move</code> can be moved <em>even after taking that value's address</em>.
That definition applies to basically all types in Rust now, so the more interesting thing to think about is what can you do with a value of a type that does not implement <code>Move</code>?</p>
<p>You end up with two phases to a value's lifetime.
There's some period in which you can move a value around, but then when you take its address the value can no longer be moved.</p>
<p>Futures in Rust work this way now.
When you create a future, you can move it around, send it to another thread (as long as it implements <code>Send</code>), put it in a data structure, etc.
But in order to call <code>poll</code>, you first have to pin the future, and once you do that no longer move the future.</p>
<p>If we had the <code>Move</code> trait instead, the <code>Future</code> trait would look like this:</p>
<pre style="background-color:#ffffff;">
<span style="font-weight:bold;color:#a71d5d;">trait </span><span style="color:#323232;">Future {
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">type </span><span style="color:#323232;">Output;
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">fn </span><span style="font-weight:bold;color:#795da3;">poll</span><span style="color:#323232;">(</span><span style="font-weight:bold;color:#a71d5d;">&amp;mut </span><span style="color:#323232;">self, cx: </span><span style="font-weight:bold;color:#a71d5d;">&amp;mut</span><span style="color:#323232;"> Context) -&gt; Poll&lt;</span><span style="font-weight:bold;color:#a71d5d;">Self::</span><span style="color:#323232;">Output&gt;;
</span><span style="color:#323232;">}
</span></pre>
<p>Notice how <code>Pin</code> has just gone away?</p>
<p>When you call the <code>poll</code> method, doing something like <code>f.poll(cx)</code>, Rust automatically borrows (i.e. takes the address of) <code>f</code> for you and passes that as the <code>self</code> argument to <code>poll</code>.
If the future does not implement <code>Move</code>, the compiler would notice that you've taken the address of <code>f</code> and prevent you from moving <code>f</code> after that point.</p>
<p>When I first heard this I thought the analysis to make this work would surely be infeasible.
It turns out it's actually not too hard and can be done entirely with local reasoning.
For example, if you've received a reference to some <code>!Move</code> type, you know it's pinned because someone had to take the address to give you a reference.
So you basically just need to track where the first place you borrow some owned type happens.
This lines up neatly with the kind of analysis the compiler is already doing in borrow checking.</p>
<h2 id="so-why-don-t-we-have-this-version-of-move">So why don't we have this version of <code>Move</code>?<a class="header-anchor" href="#so-why-don-t-we-have-this-version-of-move">🔗</a></h2>
<p>The main reason is that it's not backwards compatible.
Let's look at one of many examples of the way it's not backwards compatible.
Functions in Rust implement one or more of the various <code>Fn*</code> traits.
Each of these have an <code>Output</code> parameter that represents the return value.
For <code>!Move</code> types, we want to be able to return them from a function, such as to make a constructor function called <code>new</code>.
So this means we need to add <code>?Move</code> as a bound to the <code>Output</code> parameter of the <code>Fn*</code> traits.
But now, any type you want to have the existing behavior, where return values are movable, you'd have to add a <code>Output: Move</code> bound to your type signature.</p>
<p>This version of <code>Move</code> also struggles with projections.<sup class="footnote-reference"><a href="#boats" id="fnref:boats">1</a></sup>
Consider the following code:</p>
<pre style="background-color:#ffffff;">
<span style="font-weight:bold;color:#a71d5d;">struct </span><span style="color:#323232;">Slot&lt;T: </span><span style="background-color:#f5f5f5;font-weight:bold;color:#b52a1d;">?</span><span style="color:#323232;">Move&gt; {
</span><span style="color:#323232;">    slot: Option&lt;T&gt;
</span><span style="color:#323232;">}
</span><span style="color:#323232;">
</span><span style="font-weight:bold;color:#a71d5d;">impl</span><span style="color:#323232;">&lt;T: </span><span style="background-color:#f5f5f5;font-weight:bold;color:#b52a1d;">?</span><span style="color:#323232;">Move&gt; Slot {
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">fn </span><span style="font-weight:bold;color:#795da3;">fill_slot</span><span style="color:#323232;">(</span><span style="font-weight:bold;color:#a71d5d;">&amp;mut </span><span style="color:#323232;">self, value: T) {
</span><span style="color:#323232;">        self.slot </span><span style="font-weight:bold;color:#a71d5d;">= </span><span style="color:#0086b3;">Some</span><span style="color:#323232;">(value);
</span><span style="color:#323232;">    }
</span><span style="color:#323232;">
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">fn </span><span style="font-weight:bold;color:#795da3;">take</span><span style="color:#323232;">(</span><span style="font-weight:bold;color:#a71d5d;">&amp;mut </span><span style="color:#323232;">self) -&gt; T {
</span><span style="color:#323232;">        self.slot.</span><span style="color:#62a35c;">take</span><span style="color:#323232;">().</span><span style="color:#62a35c;">unwrap</span><span style="color:#323232;">()
</span><span style="color:#323232;">    }
</span><span style="color:#323232;">}
</span></pre>
<p>Is <code>take</code> legal?
Strictly no, because to call <code>take</code> you've exposed the address of <code>self</code>, which technically means you've exposed the address of all the fields of <code>self</code>, meaning <code>slot</code> would no longer be moveable.
This particular case happens to be safe since we can see that there's no way to actually observe the address of <code>slot</code>.
But we've now made this an interprocedural analysis instead of a local analysis.
Consider if we add the following method:</p>
<pre style="background-color:#ffffff;">
<span style="font-weight:bold;color:#a71d5d;">fn </span><span style="font-weight:bold;color:#795da3;">poll_inner</span><span style="color:#323232;">(</span><span style="font-weight:bold;color:#a71d5d;">&amp;mut </span><span style="color:#323232;">self, cx: </span><span style="font-weight:bold;color:#a71d5d;">&amp;mut</span><span style="color:#323232;"> Context) -&gt; Option&lt;Poll&lt;T&gt;&gt;
</span><span style="font-weight:bold;color:#a71d5d;">where
</span><span style="color:#323232;">    T: Future
</span><span style="color:#323232;">{
</span><span style="color:#323232;">    self.</span><span style="color:#62a35c;">map</span><span style="color:#323232;">(|f| f.</span><span style="color:#62a35c;">poll</span><span style="color:#323232;">(cx))
</span><span style="color:#323232;">}
</span></pre>
<p>Now knowing whether <code>take</code> is safe depends on whether <code>poll_inner</code> has been called.</p>
<p>This particular case is probably not insurmountable.
We'd probably just go with our initial conservative observation that <code>take</code> exposes the address of <code>slot</code> and therefore <code>slot</code> is no longer moveable.
But this also greatly limits the flexibility of the <code>Move</code> trait.</p>
<h1 id="the-new-move">The New <code>Move</code><a class="header-anchor" href="#the-new-move">🔗</a></h1>
<p>When I first started thinking about a <code>Move</code> trait, I assumed its definition was that a value of a certain type could be moved if and only if the type implements <code>Move</code>.<sup class="footnote-reference"><a href="#yosh" id="fnref:yosh">2</a></sup>
In particular, you'd need a <code>Move</code> implementation to do any of the following things:</p>
<ul>
<li>Pass a value to a function</li>
<li>Return a value from a function<sup class="footnote-reference"><a href="#feature-not-a-bug" id="fnref:feature-not-a-bug">3</a></sup></li>
<li>Use <code>mem::swap</code>, <code>mem::replace</code>, <code>mem::take</code>, or similar functions</li>
</ul>
<p>Many things about this version of the trait just work in surprisingly pleasing ways.
For example, pin projection just turns into normal projection.
You don't need any annotations to say which fields can be pinned or not because this information is carried by whether the type of the field implements <code>Move</code>.
Similarly, there's no ambiguity in the <code>Slot</code> example before; in order to write <code>take</code> you must have a <code>Move</code> bound on <code>T</code>.</p>
<p>The downside is that this doesn't naturally support the patterns where a type may move around for a while until at some point it's pinned and then remains pinned.</p>
<p>Instead we have to rely on API design and use one type to encapsulate the unpinned phase of a value's lifetime, and then use another type to encapsulate the pinned phase.
For <code>Future</code>, we already have this, with the <code>IntoFuture</code> trait being able to represent the unpinned phase and the <code>Future</code> trait representing the pinned phase.</p>
<p>How you convert from one to the other is hard though, because you can't return a <code>!Move</code> type from a function.
In stable Rust today, you could work around this using <code>MaybeUninit</code> but long term we'd need to design some kind of placement new or emplace feature.</p>
<p>What about backwards compatibility?
This formulation doesn't have the issue of needing to add <code>?Move</code> to the <code>Fn*::Output</code> types.
In fact, there aren't many places where you'd need to add the bound.
The reason is that if you aren't moving something, you are already taking a reference to it.
Reference types like <code>&amp;T</code> and <code>&amp;mut T</code> implement <code>Move</code> even if <code>T</code> does not.
So the only place where it really makes sense to add a <code>?Move</code> bound is if you are already taking a generic parameter by reference and don't need to move out of it.</p>
<p>The bigger backwards compatibility question is how you'd make <code>Move</code> interoperate well with the existing <code>Pin</code> wrapper and the functions that use it.
I haven't thought much about this problem, so that will have to wait for another post.</p>
<hr />
<div class="footnote-definition" id="boats"><sup class="footnote-definition-label">1</sup>
<p>Thanks to Boats for pointing this out to me.<a href="#fnref:boats" class="footnote-backref">↩</a></p>
</div>
<div class="footnote-definition" id="yosh"><sup class="footnote-definition-label">2</sup>
<p>This is also the definition that Yosh Wuyts was working from in his post <a href="https://blog.yoshuawuyts.com/self-referential-types/#immovable-types">Ergonomic Self-Referential Types for Rust</a>. Also, thanks for Yosh for helping me work through the ideas in this section.<a href="#fnref:yosh" class="footnote-backref">↩</a></p>
</div>
<div class="footnote-definition" id="feature-not-a-bug"><sup class="footnote-definition-label">3</sup>
<p>It's interesting that in this formulation we also cannot return a <code>!Move</code> type from a function, but in this case it's a feature rather than an insurmountable obstacle.<a href="#fnref:feature-not-a-bug" class="footnote-backref">↩</a></p>
</div>
]]></content><author><name>Eric Holk</name></author><summary type="html"><![CDATA[<p>Lately I've been talking to a few people about whether it might be possible to replace the <code>Pin</code> wrapper in Rust with a new <code>Move</code> trait.
<code>Pin</code> is one of those things that, while amazing that Rust can express the concept and semantics using just a library type, is not a lot of fun to use.
This creates friction in building out Rust's async features because currently doing so increases the opportunities for users to be exposed to <code>Pin</code>.
Maybe we can just get rid of <code>Pin</code> entirely?</p>
<p>One possible way to do this is with a trait called <code>Move</code>.
This would be a trait that most types implement and indicates that you have the ability to perform a move operation on a value of that type.
It's sort of like the <code>Send</code> trait, where when a type implements <code>Send</code> it means values of those types can be sent to another thread.</p>
<p>This trait was considered somewhere in the Rust 2015 and 2018 timeframe but was rejected in favor of <code>Pin</code> instead.
The main reason is that <code>Pin</code> could be added in a way that meets Rust's stability guarantees.
Indeed, how to add <code>Move</code> now is a huge problem that we're going to entirely ignore in this post.</p>
<p>I recently discovered that the version of <code>Move</code> that was originally considered worked quite different from how I would have expected.
As a result, we accidentally have two <code>Move</code> designs.
In this post, I want to briefly describe each one and then discuss some of the tradeoffs they make.</p>
]]></summary></entry><entry><title type="html">Some Rough Thoughts on Rust Project Organization</title><link href="https://theincredibleholk.org/blog/2024/05/06/some-rough-thoughts-on-rust-project-organization/" rel="alternate" type="text/html" title="Eric Holk"/><published>2024-05-06T00:00:00+00:00</published><updated>2024-05-06T00:00:00+00:00</updated><id>https://theincredibleholk.org/blog/2024/05/06/some-rough-thoughts-on-rust-project-organization/</id><content type="html" xml:base="https://theincredibleholk.org/blog/2024/05/06/some-rough-thoughts-on-rust-project-organization/"><![CDATA[<p>One of the big <a href="https://blog.rust-lang.org/inside-rust/2023/08/25/leadership-initiatives.html">priorities</a> for the Rust Leadership Council has been to determine the "<a href="https://github.com/rust-lang/leadership-council/issues/33">shape of Rust</a>."
For a long time I've wanted to write a comprehensive blog post about what I think the shape of Rust should look like.
Unfortunately, I've had too many competing priorities to write something fully comprehensive.
This post is not that post, but I did want to try to put down a couple ideas I've been thinking about recently.</p>
<p>I want to touch on three things.
These aren't necessarily in priority order or anything, but they are all things that I think would be useful for various reasons.
The three things in this post are:</p>
<ol>
<li>Create structures for support roles</li>
<li>Create a Design Team</li>
<li>Separate Project Membership from Team Membership</li>
</ol>
<!-- MORE -->
<h1 id="create-structures-for-support-roles">Create structures for support roles<a class="header-anchor" href="#create-structures-for-support-roles">🔗</a></h1>
<p>While I've listed this as one major idea, I think there are at least two ideas that could fit under this heading:</p>
<ul>
<li>Create a Council Staff Team</li>
<li>Provide Project-wide support</li>
</ul>
<p>But first, let's talk about support.
This area has been a bit of a revelation for me.
When talking about how to spend Rust's budget, people regularly mention hiring support staff.
I haven't been sure what we'd do with this staff, but I was curious because a lot of people I trust seemed to think it was a good idea.
Throughout my career, I often haven't had an amazing experience with roles like project managers, program managers, technical program managers, etc.
I imagine a lot of this was me not understanding the value they brought so it felt like they were just asking me to fill out TPS reports and show up at meetings to talk about all the things we haven't done since the last meeting because we were in meetings.
To the excellent people in this roles I've worked with before, I apologize for undervaluing you!</p>
<p>When people in the Rust project talk about support, there seem to be envisioning a few different things, such as:</p>
<ul>
<li><strong>Project/program management</strong>: Keep track of project status, open issues, make sure people are working on them, etc.</li>
<li><strong>Engineering support</strong>: This role would work on things like triagebot and other software tools to help the Rust project work more efficiently. One thing I'd personally love here is a way to set personal limits on my review queue, like "I'm willing to review two PRs per week."</li>
<li><strong>Secretary or Executive Assistant</strong>: Keep minutes for meetings, coordinate scheduling as needed, keep track of upcoming or recurring deadlines, etc.</li>
</ul>
<p>While my intention here is to express that these things are valuable, I'm sure I'm selling each of these roles short.
Unfortunately, this is often the kind of work that is more visible when it goes poorly than when it goes well.
I'm sure I'm missing a lot of things that support staff provide because they do it so well no one notices.</p>
<p>Anyway, let's dive into these two approaches to support roles.</p>
<h2 id="create-a-council-staff-team">Create a Council Staff Team<a class="header-anchor" href="#create-a-council-staff-team">🔗</a></h2>
<p><a href="https://rust-lang.github.io/rfcs/3392-leadership-council.html">RFC 3392</a>, which created the Rust Leadership Council, says <em>repeatedly</em> that the Council should mostly delegate rather than do the work themselves.
In my opinion, this is something that we on the Council have not done an amazing job of.
I also commonly hear people not on the Council say "I wish you would delegate more."</p>
<p>A big part of the reason why this is the case is that each time we delegate we have to decide who to delegate to.
Many times this means creating a team, or subcommittee as we've tended to call small project groups doing work on behalf of the Council.
It turns out, creating a subcommittee is a lot of work.
So we're often faced with a choice between doing a lot of work to find volunteers for a new subcommittee and then waiting for them to do the work, or just doing the work ourselves.
Because so far our subcommittees have been focused on a single project, we don't really get to reuse the work of building the subcommittee.
I could imagine if the same subcommittee did several projects, then the tradeoff looks a lot more attractive because we can amortize that effort over future projects.</p>
<p>The idea of the Council Staff Team is basically to amortize the effort of creating subcommittees.
We would find a set of people to initially fill the team and part of their charter would be to continue to find people to maintain and grow the team as needed.
The Council Staff then becomes the <em>default</em> place for the Council to delegate to when there is no obviously better place already existing.</p>
<p>The analogy I've had in mind most while thinking about this is a congressperson's staff.
At least in the United States (and I assume in many other countries), although the legislature officially has the power of writing and voting on the laws, the actual elected members do not write the thousands of pages of legislation they vote on (as far as I can tell they don't even read them).
Instead, at least in theory, they tell their staff what priorities and goals are important to their constituents (or maybe their donors instead) and ask their staff to do research and design policies that they believe will achieve these goals.</p>
<p>I imagine the Council Staff Team could work similarly.
As the Council, we would identify priorities and things that need to be solved for the project.
These things are likely to be hard and things that we as representatives of the various Rust times likely lack the necessary background to solve well.
On the other hand, the Council Staff team would ideally be filled with people who love researching and designing policies!
So they would work with the Council to draft policies to meet the goals decided by the Council.
These would then be presented back to the Council, and perhaps made into project-wide RFCs if needed, and we could decide to adopt the policy.</p>
<h2 id="provide-project-wide-support">Provide Project-wide support<a class="header-anchor" href="#provide-project-wide-support">🔗</a></h2>
<p>I'm a bit less clear on this section, but several people have raised ideas in this space and I think it warrants some exploration.</p>
<p>Several people have cropped up across the Project who have taken on either support or project management roles.
These people do things like curate the agenda for team meetings, facilitate meetings, keep track of what issues need triaged, what RFCs and decisions are in progress, and often do a lot of work unblocking progress on these things.
Everyone who works on teams with one of these volunteers has given absolutely glowing feedback on how having someone to do this work has kept things in the project moving smoothly and have accelerate the progress.
Seeing these success stories in various points across the Project is a major part of why I'm really excited about the value that support work provides!</p>
<p>I think every team should have someone who's keeping things moving smoothly like this.</p>
<p>The question is how.</p>
<p>It's tempting to make a new team, like T-operations or T-support.
The people who have been doing this work already would become founding members of this team and hopefully the team would grow over time.
We could even hire people for this role.
Then team members would sort of roam around to other teams and provide their services.</p>
<p>I think there are some ways this approach may not work as well.
It reminds me somewhat of corporate structures, where you typically have a dev organization and a PM organization.
The PMs are assigned to a certain dev team, but because their have their own separate reporting chain, this can lead to the PMs' goals and the devs' not being aligned.</p>
<p>It's easy to focus on the org chart and say things like "Oh, we want more people doing project support work, so let's make a team."
This may be the right answer, but I think there are some more fundamental goals that should drive this decision.</p>
<p>First, these support people need to be deeply embedded in the team they support.
Some of the work looks somewhat standalone at first.
For example, building a triage agenda can be done by pulling a couple GitHub labels and putting them in a HackMD.
Indeed, we have several bots that do this.
However, this work often requires much more context to be maintained, and this context is not always visible.
This is especially true when it comes to decisions where there are competing proposals.
One of the things the people doing support work that I've worked with in the Rust project do is compile lists of various positions and the major concerns and values driving those positions.
They actively seek ways to find common ground and unblock things.
In my experience, they know the nuances of the issues they are bringing for discussion better than anyone else.
Doing this work <em>requires</em> close ties to the team.</p>
<p>Second, we need to make sure we have ways to sustain and grow this work.
This is hard, unrelenting work, and I suspect prone to burnout.
We want to make sure people doing support work are themselves supported and appreciated.
Where it makes sense, this should include financial support.
There are needs for more of this kind of work across the project, so we need to inspire more people to contribute in this way.
This is one area where a centralized operations team could help, since the team would be able to mentor contributors doing this work.
I think as a matter of project culture, we should make an effort to highlight this work that's being done (because it's easy to overlook), to celebrate it, and to appreciate those who do it.</p>
<h1 id="create-a-design-team">Create a Design Team<a class="header-anchor" href="#create-a-design-team">🔗</a></h1>
<p>Another thing I'd like to see is the creation of T-design.
The goal here is to encourage a unified design of Rust as a product.</p>
<p>I think the development process of Rust has really shone when it has been able to do codesign between the language and the standard library.</p>
<p>One early instance of this is when the <code>Vec</code> type became just a regular type in the standard library.
Earlier, <code>Vec</code> was a primitive type built into the compiler.
When you did an <code>push</code> operation, for example, the compiler would plop down some LLVM code right there to do the operation.
As the language got more powerful, we realized we could move <code>Vec</code> to the standard library.
This made <code>Vec</code> easier to implement and maintain, since it was just regular Rust code; safer, because it was written in Rust and automatically got all the safety guarantees Rust gives you; and faster, because... well, I'm not sure why we couldn't have done the same optimizations in LLVM but I recall the library version <em>was</em> faster.<sup class="footnote-reference"><a href="#vec-stable-abi" id="fnref:vec-stable-abi">1</a></sup></p>
<p>Another instance is the addition of the <code>?</code> operator.
The <code>Result</code> type had been well-built in the standard library, but using it was still a little clunky.
Adding the <code>?</code> operator to the language worked hand-in-hand with <code>Result</code> that was already in the library to make for a <em>much nicer</em> experience.</p>
<p>I want to make sure Rust is able to always do this kind of codesign, and pulling the teams that own the design of Rust under one umbrella seems like an obvious way to do this.
To start with, I'd bring T-lang and T-libs-api under the T-design umbrella.
These are the design-oriented teams I happen to be most familiar with, but others have suggested to me that tools like Cargo have design needs that could also fit here.</p>
<p>Of course, this raises the question of how to operationalize this change.</p>
<p>One option is to merge T-lang and T-libs-api into one team, so we no longer talk about either team separately but only talk about T-design.
I don't think this would be ideal.
The teams exist now because they have different goals, needs, work, etc.
They also have well-established ways of working that I wouldn't want to disrupt too much.
The goal is to improve what's already good, not start over.</p>
<p>Instead, I imagine T-design being a very small container team that continues to delegate almost all of its purview to T-lang and T-libs-api.</p>
<p>But then, who is would be a member of T-design, and what powers would they retain?
I would start with a small number of people drawn from T-lang and T-libs-api who have deep experience with the design of Rust and a long tenure in the project.
They wouldn't be involved with the day-to-day design work; if they want to do that they would also be members of the subteams.
But they would be responsible for making sure the Rust, both the language and standard library, feel like a unified whole and that all the parts support each other.
In concrete terms, I imagine this happens through being able to raise blocking objections for either team if something violates the overall unity of Rust's design, and also through owning decisions in cases where the language and library elements of the design are particularly inseparable.</p>
<h2 id="what-about-t-impl">What about T-impl?<a class="header-anchor" href="#what-about-t-impl">🔗</a></h2>
<p>When I first started floating this idea, I phrased it as building two top level teams: T-design and T-impl.
T-design would be, as described in the previous section, made up of T-lang and T-libs-api, and would own the overall design of the language and libraries.
T-lang would consist primarily of T-compiler and T-libs, and they would implement the design that is created by T-design.</p>
<p>There's a nice symmetry to this, but at this point, I think the motivation for T-impl is not as strong.</p>
<p>There's a lot of similarity in that, oversimplifying a lot, they both write lots of code.
But from what I can see, this code does not really need to be developed together.
Once the compiler implements new language features, the standard library can start to use them.
If those features enable new library features, then the compiler can use those.</p>
<p>I guess one case where a T-impl team would make sense is if there were cross cutting concerns that needed to be managed in a centralized way.
In corporate environments, for example, security concerns often have this shape.</p>
<p>Still, at this point in time I see less benefit to reorganizing the implementation-focused teams, while I do see benefits to bringing the design-focused teams closer together.</p>
<h1 id="separate-project-membership-from-team-membership">Separate Project Membership from Team Membership<a class="header-anchor" href="#separate-project-membership-from-team-membership">🔗</a></h1>
<p>This last section is the one I'm by far the least confident in, but I want to suggest the idea anyway, discuss the goals underlying it, and other options that might work instead.
I think I first heard this idea floated at the recent Rust Leads Summit.</p>
<p>Right now, Project membership is essentially the union of all the various Rust teams.
I would suggest refactoring these a bit.
Under this proposal, we'd have a set of people called the Rust Project Members.
Team members would then be pulled from that set.</p>
<p>The first order goal here is to make it easy to recognize people who contribute heavily to the Rust project in various ways, often across teams, but may not have yet been invited to a maintainership role on a Team.
I'm unclear on what would be the exact requirements or procedures for becoming a member, but it's roughly someone who is recognized by other members (perhaps Team members) as someone who contributes recognizably and significantly to the project.</p>
<p>Underlying this goal are a couple others.</p>
<p>One, the Foundation often needs to know who the Project Members are.
Part of their mission is to support the set of maintainers governing and developing the project.
In order to do this, they need to know who those people are.
So far we've been using anyone on the <a href="mailto:all@rust-lang.org">all@rust-lang.org</a> mailing list as our working definition, but this is not an ideal set.
In particular, this mailing list does not include everyone who is developing the project and should be eligible for Foundation support.
If we created a clear group of Rust Project Members, which includes all the maintainers (roughly Team Members now) and also others making significant contributions, then we have a much better set to give the Foundation when needed.</p>
<p>Part of why this matters is that the Foundation is looking at more ways to support Project Members.
While nothing is set in stone yet, this could include things like helping to buy a headset for someone who has a less than ideal audio situation but wants to participate in team calls, or helping to cover travel costs for Project Members to attend conferences.</p>
<p>And this leads into my second motivation, which is to remove friction from supporting the Project.
The Foundation has a lot of resources that it can use to support the project.
One of my biggest concerns is that we'll spend too much time talking about how to best support the Project and then not spend any of the money or use any of the other resources we have.
I'd rather make it easy to use the resources.
We may not always use them in the very best way possible, but we can learn from these mistakes and be more effective going forward.</p>
<p>To this end, if we have some kind of support available to the Project, like hardware or travel support, I'd like to make the decision as much of a rubber stamp as possible.
If a request comes in, I want to say "this support is available to Project Members? Are you a Project Member? Yes? Okay, here you go."
The alternative is that we need to have some more in depth application and review process.
This discourages people from applying in the first place, and adds some ambiguity to the application review process.</p>
<p>Of course, there are challenges.
Having low friction like this means it's imperative that the Project is a high trust organization.</p>
<p>If we had a general set of Project Members, we need to make sure the process for including people in it are fair.
We also need a regular review period to make sure the roster stays up to date.
It can be awkward if someone needs to be removed.
In effect, all the challenges I mentioned with reviewing applications have to instead be resolved at the point where someone wants to become a member of the Project.</p>
<p>And there are other ways to accomplish these goals as well.
For example, more teams are adding T-*-contributors teams.
In effect, what I'm proposing as the Project Members group would be the union of all the T-*-contributors teams.</p>
<p>And this proposal may not address the other real problems.
For example, contributors do not always want to ask for membership, and teams can sometimes overlook adding someone who has been significantly contributing for some time.
It might be better to directly address the questions and processes for team membership rather than creating a new organizational structure and doing it as part of that process.</p>
<h1 id="conclusion">Conclusion<a class="header-anchor" href="#conclusion">🔗</a></h1>
<p>One of the tasks the Leadership Council was encouraged to do, and has decided to do, is to consider the boundaries and organization of the Rust Project.
This is obviously a huge project.</p>
<p>In this post, I've shared some ideas in this space that I think are worthy of consideration.
I'm sharing them here in part so that others can consider them, but also to spark discussion, brainstorming, and alternate proposals.</p>
<p>These have some pros that I hope I've laid out, but there are also open questions that I've also touched on.
I'd love to hear other folks' ideas on how to effectively organize the Rust Project!</p>
<hr />
<div class="footnote-definition" id="vec-stable-abi"><sup class="footnote-definition-label">1</sup>
<p>I didn't realize this at the time, but Graydon <a href="https://graydon2.dreamwidth.org/307291.html#:~:text=Library%2Ddefined%20containers%2C%20iteration%20and%20smart%20pointers">points out</a> a downside of this decision is that it made it harder to give Rust a stable ABI.<a href="#fnref:vec-stable-abi" class="footnote-backref">↩</a></p>
</div>
]]></content><author><name>Eric Holk</name></author><summary type="html"><![CDATA[<p>One of the big <a href="https://blog.rust-lang.org/inside-rust/2023/08/25/leadership-initiatives.html">priorities</a> for the Rust Leadership Council has been to determine the "<a href="https://github.com/rust-lang/leadership-council/issues/33">shape of Rust</a>."
For a long time I've wanted to write a comprehensive blog post about what I think the shape of Rust should look like.
Unfortunately, I've had too many competing priorities to write something fully comprehensive.
This post is not that post, but I did want to try to put down a couple ideas I've been thinking about recently.</p>
<p>I want to touch on three things.
These aren't necessarily in priority order or anything, but they are all things that I think would be useful for various reasons.
The three things in this post are:</p>
<ol>
<li>Create structures for support roles</li>
<li>Create a Design Team</li>
<li>Separate Project Membership from Team Membership</li>
</ol>
]]></summary></entry><entry><title type="html">A Rose By Any Other Name</title><link href="https://theincredibleholk.org/blog/2024/04/19/a-rose-by-any-other-name/" rel="alternate" type="text/html" title="Eric Holk"/><published>2024-04-19T00:00:00+00:00</published><updated>2024-04-19T00:00:00+00:00</updated><id>https://theincredibleholk.org/blog/2024/04/19/a-rose-by-any-other-name/</id><content type="html" xml:base="https://theincredibleholk.org/blog/2024/04/19/a-rose-by-any-other-name/"><![CDATA[<p>There are currently two competing designs for async iteration traits for Rust.
The first is <code>poll_next</code>.
The second is <code>async fn next</code>.
I see strengths to each design.
The <code>poll_next</code> design seems stronger on technical concerns, such as performance and ease of implementation.
The <code>async fn next</code> design seems better from an ergonomics and consistency perspective.
Unfortunately, the process of resolve this debate has been slow going.</p>
<p>One thing I've realized in the debate is that at this point the two designs are more similar than they may seem at first glance.
In this post I'd like to show how a handful of minor tweaks to each design results in something that is the same modulo names.</p>
<!-- MORE -->
<h1 id="a-concrete-performance-difference">A Concrete Performance Difference<a class="header-anchor" href="#a-concrete-performance-difference">🔗</a></h1>
<p>But first, we're going to go on a bit of a diversion about the performance of the two designs.</p>
<p>One of the early arguments in favor of <code>poll_next</code> was that it was more efficient.
In most cases, we expect the compiler to be able to optimize away any of the overhead that <code>async fn next</code> might introduce, but it seems better, all else being equal, to pick the design that doesn't require as much work from the compiler.
That said, we discovered one case where the compiler cannot optimize away the overhead of <code>async fn next</code>, and that has to do with the using a <code>dyn AsyncIterator</code> object.</p>
<p>To see the difference, let's consider pseudo-code for a <code>for await</code> loop for the two designs.
We'll also desugar the <code>.await</code> calls so we can see where the calls to <code>poll</code> happen.</p>
<p>Let's consider a simple function that receives a <code>dyn AsyncIterator</code> and iterates over it:</p>
<pre style="background-color:#ffffff;">
<span style="color:#323232;">async </span><span style="font-weight:bold;color:#a71d5d;">fn </span><span style="font-weight:bold;color:#795da3;">sum</span><span style="color:#323232;">(it: </span><span style="font-weight:bold;color:#a71d5d;">&amp;</span><span style="color:#323232;">Box&lt;dyn AsyncIterator&lt;Item = </span><span style="font-weight:bold;color:#a71d5d;">i32</span><span style="color:#323232;">&gt;&gt;) -&gt; </span><span style="font-weight:bold;color:#a71d5d;">i32 </span><span style="color:#323232;">{
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">let mut</span><span style="color:#323232;"> total </span><span style="font-weight:bold;color:#a71d5d;">= </span><span style="color:#0086b3;">0</span><span style="color:#323232;">;
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">for</span><span style="color:#323232;"> await i </span><span style="font-weight:bold;color:#a71d5d;">in</span><span style="color:#323232;"> it {
</span><span style="color:#323232;">        total </span><span style="font-weight:bold;color:#a71d5d;">+=</span><span style="color:#323232;"> i;
</span><span style="color:#323232;">    }
</span><span style="color:#323232;">    total
</span><span style="color:#323232;">}
</span></pre>
<p>Using the <code>poll_next</code> design, this desugars to something like follows.
I've left out the context parameter to <code>poll_next</code> for simplicity.</p>
<pre style="background-color:#ffffff;">
<span style="color:#323232;">async </span><span style="font-weight:bold;color:#a71d5d;">fn </span><span style="font-weight:bold;color:#795da3;">sum</span><span style="color:#323232;">(it: Box&lt;dyn AsyncIterator&lt;Item = </span><span style="font-weight:bold;color:#a71d5d;">i32</span><span style="color:#323232;">&gt;&gt;) -&gt; </span><span style="font-weight:bold;color:#a71d5d;">i32 </span><span style="color:#323232;">{
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">let mut</span><span style="color:#323232;"> total </span><span style="font-weight:bold;color:#a71d5d;">= </span><span style="color:#0086b3;">0</span><span style="color:#323232;">;
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">let</span><span style="color:#323232;"> it </span><span style="font-weight:bold;color:#a71d5d;">= </span><span style="color:#0086b3;">Box</span><span style="color:#323232;">::into_pin(it);
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">loop </span><span style="color:#323232;">{
</span><span style="color:#323232;">        </span><span style="font-weight:bold;color:#a71d5d;">match</span><span style="color:#323232;"> it.</span><span style="color:#62a35c;">poll_next</span><span style="color:#323232;">() {  </span><span style="font-style:italic;color:#969896;">// poll_next call is indirect
</span><span style="color:#323232;">            Poll::Ready(</span><span style="color:#0086b3;">Some</span><span style="color:#323232;">(i)) </span><span style="font-weight:bold;color:#a71d5d;">=&gt;</span><span style="color:#323232;"> total </span><span style="font-weight:bold;color:#a71d5d;">+=</span><span style="color:#323232;"> i,
</span><span style="color:#323232;">            Poll::Ready(</span><span style="color:#0086b3;">None</span><span style="color:#323232;">) </span><span style="font-weight:bold;color:#a71d5d;">=&gt; break</span><span style="color:#323232;">,
</span><span style="color:#323232;">            Poll::Pending </span><span style="font-weight:bold;color:#a71d5d;">=&gt; </span><span style="background-color:#f5f5f5;font-weight:bold;color:#b52a1d;">yield</span><span style="color:#323232;"> Poll::Pending,
</span><span style="color:#323232;">        }
</span><span style="color:#323232;">    }
</span><span style="color:#323232;">    total
</span><span style="color:#323232;">}
</span></pre>
<p>Using the <code>async fn next</code> design, this desugars to something like below.
Note that the in this version <code>AsyncIterator</code> is not object safe, so we're assuming we have <a href="/blog/2022/04/18/how-async-functions-in-traits-could-work-in-rustc/#dynamic-traits-with">support using something like <code>dyn*</code></a>.</p>
<pre style="background-color:#ffffff;">
<span style="color:#323232;">async </span><span style="font-weight:bold;color:#a71d5d;">fn </span><span style="font-weight:bold;color:#795da3;">sum</span><span style="color:#323232;">(it: Box&lt;dyn AsyncIterator&lt;Item = </span><span style="font-weight:bold;color:#a71d5d;">i32</span><span style="color:#323232;">&gt;&gt;) -&gt; </span><span style="font-weight:bold;color:#a71d5d;">i32 </span><span style="color:#323232;">{
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">let mut</span><span style="color:#323232;"> total </span><span style="font-weight:bold;color:#a71d5d;">= </span><span style="color:#0086b3;">0</span><span style="color:#323232;">;
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">loop </span><span style="color:#323232;">{
</span><span style="color:#323232;">        </span><span style="font-weight:bold;color:#a71d5d;">let</span><span style="color:#323232;"> f: Pin&lt;dyn</span><span style="background-color:#f5f5f5;font-weight:bold;color:#b52a1d;">*</span><span style="color:#323232;"> Future&lt;Output = Option&lt;</span><span style="font-weight:bold;color:#a71d5d;">i32</span><span style="color:#323232;">&gt;&gt;</span><span style="font-weight:bold;color:#a71d5d;">&gt; = </span><span style="color:#323232;">pin!(it.</span><span style="color:#62a35c;">next</span><span style="color:#323232;">()); </span><span style="font-style:italic;color:#969896;">// next call is indirect
</span><span style="color:#323232;">        </span><span style="font-weight:bold;color:#a71d5d;">let</span><span style="color:#323232;"> next </span><span style="font-weight:bold;color:#a71d5d;">= loop </span><span style="color:#323232;">{
</span><span style="color:#323232;">            </span><span style="font-weight:bold;color:#a71d5d;">match</span><span style="color:#323232;"> f.</span><span style="color:#62a35c;">poll</span><span style="color:#323232;">() { </span><span style="font-style:italic;color:#969896;">// poll call is indirect
</span><span style="color:#323232;">                Poll::Ready(next) </span><span style="font-weight:bold;color:#a71d5d;">=&gt; break</span><span style="color:#323232;"> next,
</span><span style="color:#323232;">                Poll::Pending </span><span style="font-weight:bold;color:#a71d5d;">=&gt; </span><span style="background-color:#f5f5f5;font-weight:bold;color:#b52a1d;">yield</span><span style="color:#323232;"> Poll::Pending,
</span><span style="color:#323232;">            }
</span><span style="color:#323232;">        };
</span><span style="color:#323232;">        </span><span style="font-weight:bold;color:#a71d5d;">match</span><span style="color:#323232;"> next {
</span><span style="color:#323232;">            </span><span style="color:#0086b3;">Some</span><span style="color:#323232;">(i) </span><span style="font-weight:bold;color:#a71d5d;">=&gt;</span><span style="color:#323232;"> total </span><span style="font-weight:bold;color:#a71d5d;">+=</span><span style="color:#323232;"> i,
</span><span style="color:#323232;">            </span><span style="color:#0086b3;">None </span><span style="font-weight:bold;color:#a71d5d;">=&gt; break</span><span style="color:#323232;">,
</span><span style="color:#323232;">        }
</span><span style="color:#323232;">    }
</span><span style="color:#323232;">    total
</span><span style="color:#323232;">}
</span></pre>
<p>The <code>async fn next</code> version has two indirect calls in its desugaring.
First need to make an indirect call to <code>next</code> to get the future to poll to get the actual next item.
The way async in trait objects works means that the resulting future will itself be a trait object, which means calling <code>poll</code> on that future will be an indirect call too.</p>
<p>It's possible these calls don't matter.
Indirect calls are normally only relevant in CPU-bound workloads, while async is most often used for IO-bound workloads.
In wg-async, we've talked a lot about the this extra indirections but I think it's mostly because we can objectively count how many indirect calls there are, while we can't objectively things like design elegance or ergonomics.</p>
<p>There will be cases where this overhead does matter though, so it is desirable to not bake these calls in at the language level.
Can we tweak the design of <code>async fn next</code> to remove most of this overhead?</p>
<h1 id="reducing-the-async-fn-next-overhead">Reducing the <code>async fn next</code> overhead<a class="header-anchor" href="#reducing-the-async-fn-next-overhead">🔗</a></h1>
<p>The key idea here is to change the semantics of the future returned by <code>async fn next</code> so that it's able to be polled again after completion.
When polled after completion, it essentially calls <code>next</code> again but reuses the future.
This is, in fact, how the <a href="https://docs.rs/futures-util/0.3.30/src/futures_util/stream/stream/next.rs.html#31"><code>Next</code> future from the futures crate</a> works.
It wraps <code>poll_next</code>, so the future has the same semantics as <code>poll_next</code>.</p>
<p>If we make this semantic change, then we can desugar the <code>async fn next</code> version to something like below.</p>
<pre style="background-color:#ffffff;">
<span style="color:#323232;">async </span><span style="font-weight:bold;color:#a71d5d;">fn </span><span style="font-weight:bold;color:#795da3;">sum</span><span style="color:#323232;">(it: Box&lt;dyn AsyncIterator&lt;Item = </span><span style="font-weight:bold;color:#a71d5d;">i32</span><span style="color:#323232;">&gt;&gt;) -&gt; </span><span style="font-weight:bold;color:#a71d5d;">i32 </span><span style="color:#323232;">{
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">let mut</span><span style="color:#323232;"> total </span><span style="font-weight:bold;color:#a71d5d;">= </span><span style="color:#0086b3;">0</span><span style="color:#323232;">;
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">let mut</span><span style="color:#323232;"> f: Pin&lt;dyn</span><span style="background-color:#f5f5f5;font-weight:bold;color:#b52a1d;">*</span><span style="color:#323232;"> Future&lt;Output = Option&lt;</span><span style="font-weight:bold;color:#a71d5d;">i32</span><span style="color:#323232;">&gt;&gt;</span><span style="font-weight:bold;color:#a71d5d;">&gt; = </span><span style="color:#323232;">pin!(it.</span><span style="color:#62a35c;">next</span><span style="color:#323232;">()); </span><span style="font-style:italic;color:#969896;">// next call is indirect
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">loop </span><span style="color:#323232;">{
</span><span style="color:#323232;">        </span><span style="font-weight:bold;color:#a71d5d;">let</span><span style="color:#323232;"> next </span><span style="font-weight:bold;color:#a71d5d;">= loop </span><span style="color:#323232;">{
</span><span style="color:#323232;">            </span><span style="font-weight:bold;color:#a71d5d;">match</span><span style="color:#323232;"> f.</span><span style="color:#62a35c;">poll</span><span style="color:#323232;">() { </span><span style="font-style:italic;color:#969896;">// poll call is indirect
</span><span style="color:#323232;">                Poll::Ready(next) </span><span style="font-weight:bold;color:#a71d5d;">=&gt; break</span><span style="color:#323232;"> next,
</span><span style="color:#323232;">                Poll::Pending </span><span style="font-weight:bold;color:#a71d5d;">=&gt; </span><span style="background-color:#f5f5f5;font-weight:bold;color:#b52a1d;">yield</span><span style="color:#323232;"> Poll::Pending,
</span><span style="color:#323232;">            }
</span><span style="color:#323232;">        };
</span><span style="color:#323232;">        </span><span style="font-weight:bold;color:#a71d5d;">match</span><span style="color:#323232;"> next {
</span><span style="color:#323232;">            </span><span style="color:#0086b3;">Some</span><span style="color:#323232;">(i) </span><span style="font-weight:bold;color:#a71d5d;">=&gt;</span><span style="color:#323232;"> total </span><span style="font-weight:bold;color:#a71d5d;">+=</span><span style="color:#323232;"> i,
</span><span style="color:#323232;">            </span><span style="color:#0086b3;">None </span><span style="font-weight:bold;color:#a71d5d;">=&gt; break</span><span style="color:#323232;">,
</span><span style="color:#323232;">        }
</span><span style="color:#323232;">        </span><span style="font-style:italic;color:#969896;">// the next time around the loop we&#39;ll use `f` again
</span><span style="color:#323232;">    }
</span><span style="color:#323232;">    total
</span><span style="color:#323232;">}
</span></pre>
<p>We still have two distinct indirect calls, but the first one is only called once at the start of the loop rather than inside the loop for each iteration.</p>
<p>This code is now more convoluted than it needs to be.
We can instead refactor it to only have one loop and one match expression:</p>
<pre style="background-color:#ffffff;">
<span style="color:#323232;">async </span><span style="font-weight:bold;color:#a71d5d;">fn </span><span style="font-weight:bold;color:#795da3;">sum</span><span style="color:#323232;">(it: Box&lt;dyn AsyncIterator&lt;Item = </span><span style="font-weight:bold;color:#a71d5d;">i32</span><span style="color:#323232;">&gt;&gt;) -&gt; </span><span style="font-weight:bold;color:#a71d5d;">i32 </span><span style="color:#323232;">{
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">let mut</span><span style="color:#323232;"> total </span><span style="font-weight:bold;color:#a71d5d;">= </span><span style="color:#0086b3;">0</span><span style="color:#323232;">;
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">let mut</span><span style="color:#323232;"> f: Pin&lt;dyn</span><span style="background-color:#f5f5f5;font-weight:bold;color:#b52a1d;">*</span><span style="color:#323232;"> Future&lt;Output = Option&lt;</span><span style="font-weight:bold;color:#a71d5d;">i32</span><span style="color:#323232;">&gt;&gt;</span><span style="font-weight:bold;color:#a71d5d;">&gt; = </span><span style="color:#323232;">pin!(it.</span><span style="color:#62a35c;">next</span><span style="color:#323232;">()); </span><span style="font-style:italic;color:#969896;">// next call is indirect
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">loop </span><span style="color:#323232;">{
</span><span style="color:#323232;">        </span><span style="font-weight:bold;color:#a71d5d;">match</span><span style="color:#323232;"> f.</span><span style="color:#62a35c;">poll</span><span style="color:#323232;">() {  </span><span style="font-style:italic;color:#969896;">// poll call is indirect
</span><span style="color:#323232;">            Poll::Ready(</span><span style="color:#0086b3;">Some</span><span style="color:#323232;">(i)) </span><span style="font-weight:bold;color:#a71d5d;">=&gt;</span><span style="color:#323232;"> total </span><span style="font-weight:bold;color:#a71d5d;">+=</span><span style="color:#323232;"> i,
</span><span style="color:#323232;">            Poll::Ready(</span><span style="color:#0086b3;">None</span><span style="color:#323232;">) </span><span style="font-weight:bold;color:#a71d5d;">=&gt; break</span><span style="color:#323232;">,
</span><span style="color:#323232;">            Poll::Pending </span><span style="font-weight:bold;color:#a71d5d;">=&gt; </span><span style="background-color:#f5f5f5;font-weight:bold;color:#b52a1d;">yield</span><span style="color:#323232;"> Poll::Pending,
</span><span style="color:#323232;">        }
</span><span style="color:#323232;">    }
</span><span style="color:#323232;">    total
</span><span style="color:#323232;">}
</span></pre>
<p>This version looks suspiciously like the <code>poll_next</code> desugaring.<sup class="footnote-reference"><a href="#copy-paste" id="fnref:copy-paste">1</a></sup></p>
<h2 id="aside-this-doesn-t-actually-work">Aside: This Doesn't Actually Work<a class="header-anchor" href="#aside-this-doesn-t-actually-work">🔗</a></h2>
<p>This optimization sounds good, but it doesn't actually work.
I want to briefly touch on why but then let's just pretend we didn't notice this because I'd like to consider the rest of the argument.</p>
<p>The problem is that <code>async fn</code>s only complete once.
We might redefine the semantics of <code>Future</code> to allow multi-completion futures, but unless we also change <code>async fn</code>, the futures produced by async functions will still be single-completion.
The key benefit of the <code>async fn next</code> is that users can hand-roll their own async iterators by writing <code>async fn next</code> and that this should be simpler than writing two state machines at once with a hand-written <code>poll_next</code>.
We could add a modified version of <code>async fn</code> that lets you write a multi-completion future, but if we do this I'm pretty sure we'll end up with <code>async gen fn</code> with some different names.
Or we could add a way to create a multi-completion future from an async closure, but if we make users go through all of this, we've given up the simplicity benefits of being able to just write <code>async fn next</code>.</p>
<p>Anyway, let's ignore this for now and continue on.</p>
<h1 id="iterator-setup">Iterator Setup<a class="header-anchor" href="#iterator-setup">🔗</a></h1>
<p>So with the performance optimization, things are looking very similar.
Both versions start with a little bit of setup code.
The <code>poll_next</code> version needs to pin the iterator since <code>poll_next</code> takes a pinned argument.
The <code>async fn next</code> version needs to call <code>next</code> once to get the future to poll, and then pins that as well.
Then both versions go into a loop calling either <code>poll_next</code>, or a <code>poll</code> function that we've redefined to have the same semantics as <code>poll_next</code>.</p>
<p>The <code>poll_next</code> version does not have an analog to the <code>next</code> call in the <code>async fn next</code> version.
I think this does exist, it's just not shown here.
At some point, you need to set up your iterator state.
You can't just call <code>poll_next</code> on a <code>Vec</code>, partly because the <code>Vec</code> would have to be pinned and that would be inconvenient, and also because it's not ideal to keep your iteration state attached to the <code>Vec</code>.</p>
<p>So let's imagine we have some operation which sets up the state needed to run an async iterator.
In our <code>poll_next</code> examples, we've been assuming the iterator is set up before the call to <code>sum</code>.
In other words, it's called as <code>sum(my_vec.async_iter())</code> and not <code>sum(my_vec)</code>.</p>
<p>What if, instead, we created some generic way to make an async iterator for some collection and moved that into the <code>sum</code> function?
We could call this trait <code>IntoAsyncIterator</code><sup class="footnote-reference"><a href="#intoasynciterator" id="fnref:intoasynciterator">2</a></sup>, and it would look something like:</p>
<pre style="background-color:#ffffff;">
<span style="font-weight:bold;color:#a71d5d;">trait </span><span style="color:#323232;">IntoAsyncIterator {
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">type </span><span style="color:#323232;">Item;
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">type </span><span style="color:#323232;">AsyncIter: AsyncIterator&lt;Item = </span><span style="font-weight:bold;color:#a71d5d;">Self::</span><span style="color:#323232;">Item&gt;;
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">fn </span><span style="font-weight:bold;color:#795da3;">into_async_iter</span><span style="color:#323232;">(self) -&gt; </span><span style="font-weight:bold;color:#a71d5d;">Self::</span><span style="color:#323232;">AsyncIter;
</span><span style="color:#323232;">}
</span></pre>
<p>Then we could write the <code>poll_next</code> version of <code>sum</code> as:</p>
<pre style="background-color:#ffffff;">
<span style="color:#323232;">async </span><span style="font-weight:bold;color:#a71d5d;">fn </span><span style="font-weight:bold;color:#795da3;">sum</span><span style="color:#323232;">(it: Box&lt;dyn IntoAsyncIterator&lt;Item = </span><span style="font-weight:bold;color:#a71d5d;">i32</span><span style="color:#323232;">&gt;&gt;) -&gt; </span><span style="font-weight:bold;color:#a71d5d;">i32 </span><span style="color:#323232;">{
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">let mut</span><span style="color:#323232;"> total </span><span style="font-weight:bold;color:#a71d5d;">= </span><span style="color:#0086b3;">0</span><span style="color:#323232;">;
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">let mut</span><span style="color:#323232;"> f: Pin&lt;dyn</span><span style="background-color:#f5f5f5;font-weight:bold;color:#b52a1d;">*</span><span style="color:#323232;"> AsyncIterator&lt;Item = </span><span style="font-weight:bold;color:#a71d5d;">i32</span><span style="color:#323232;">&gt;</span><span style="font-weight:bold;color:#a71d5d;">&gt; = </span><span style="color:#323232;">pin!(it.</span><span style="color:#62a35c;">into_async_iter</span><span style="color:#323232;">()); </span><span style="font-style:italic;color:#969896;">// into_async_iter call is indirect
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">loop </span><span style="color:#323232;">{
</span><span style="color:#323232;">        </span><span style="font-weight:bold;color:#a71d5d;">match</span><span style="color:#323232;"> f.</span><span style="color:#62a35c;">poll_next</span><span style="color:#323232;">() {  </span><span style="font-style:italic;color:#969896;">// poll_next call is indirect
</span><span style="color:#323232;">            Poll::Ready(</span><span style="color:#0086b3;">Some</span><span style="color:#323232;">(i)) </span><span style="font-weight:bold;color:#a71d5d;">=&gt;</span><span style="color:#323232;"> total </span><span style="font-weight:bold;color:#a71d5d;">+=</span><span style="color:#323232;"> i,
</span><span style="color:#323232;">            Poll::Ready(</span><span style="color:#0086b3;">None</span><span style="color:#323232;">) </span><span style="font-weight:bold;color:#a71d5d;">=&gt; break</span><span style="color:#323232;">,
</span><span style="color:#323232;">            Poll::Pending </span><span style="font-weight:bold;color:#a71d5d;">=&gt; </span><span style="background-color:#f5f5f5;font-weight:bold;color:#b52a1d;">yield</span><span style="color:#323232;"> Poll::Pending,
</span><span style="color:#323232;">        }
</span><span style="color:#323232;">    }
</span><span style="color:#323232;">    total
</span><span style="color:#323232;">}
</span></pre>
<p>Now we basically have the same code as the <code>async fn next</code> version.
There's a subtle difference, since <code>async fn next</code> takes <code>&amp;mut self</code> while <code>into_async_iter</code> takes <code>self</code>, but we could design the "initialize async iterator state" operation to take <code>&amp;mut self</code> if we wanted.
Also, I should point out that <code>self</code> methods are not allowed in object-safe traits, so as designed, we would not be able to have a <code>dyn IntoAsyncIterator</code>.</p>
<h1 id="what-s-in-a-name">What's in a name?<a class="header-anchor" href="#what-s-in-a-name">🔗</a></h1>
<p>If we make the change to <code>async fn next</code> to allow the future to be polled again after completion and we add some kind of <code>IntoAsyncIterator</code>, then the two designs are basically equivalent.
In both of them, the <code>for await</code> loop desugars into a setup phase (similar to have synchronous <code>for</code> loops call <code>IntoIterator</code>), and then there is a loop can calls a method to advance the state of the iterator.</p>
<p>If we make these two changes, then <code>IntoAsyncIterator</code> in the <code>poll_next</code> design is essentially what <code>AsyncIterator</code> is in the <code>async fn next</code> design.
They are both the way to initialize iterator state.
Then the <code>AsyncIterator</code> trait in the <code>poll_next</code> design is equivalent to the modified <code>Future</code> semantics in the <code>async fn next</code> design.<sup class="footnote-reference"><a href="#multi-future" id="fnref:multi-future">3</a></sup></p>
<p>So to me it seems that if we apply the multi-completion optimization to <code>async fn next</code> then the two designs have the same shape and most of the debate is around the boundaries and names of various subcomponents.</p>
<h1 id="conclusion">Conclusion<a class="header-anchor" href="#conclusion">🔗</a></h1>
<p>I wrote the first draft of this post about three months ago and then hesitated to publish it because in writing it I discovered some fatal flaws in the argument.
However, I've shared the draft privately with a few people and we've been referring to it often in discussions so I felt like it was worth putting it out in public.
Others have come up with similar arguments to this post so I consider this post to be my particular take on the argument rather than the definitive statement.</p>
<p>At the beginning I had hoped to say that with the optimizations we've added to the <code>async fn next</code> design, the two designs are essentially the same.
The implication then would be that we should stop arguing about names, pick something, and stabilize it.</p>
<p>Having written the post, I think we instead see an irreconcilable difference in the designs.
While the multi-completion future optimization does lead to a design that's equivalent to <code>poll_next</code>, it gives up the key characteristic of the <code>async fn next</code> design, which is the ability to hand-write iterators with <code>async fn next</code>.
It's not "here's a small tweak and now the designs are the same," it's "here's a small tweak and now <code>async fn next</code> is a completely different design."</p>
<p>So in conclusion, the two designs are not secretly the same and we are going to have to actually make a decision on which one we want to have.</p>
<hr />
<div class="footnote-definition" id="copy-paste"><sup class="footnote-definition-label">1</sup>
<p>In fact, I copied the <code>poll_next</code> desugaring and then made a few changes to get this version.<a href="#fnref:copy-paste" class="footnote-backref">↩</a></p>
</div>
<div class="footnote-definition" id="intoasynciterator"><sup class="footnote-definition-label">2</sup>
<p><code>IntoAsyncIterator</code> may not be the best name, because it implies it consumes its source. We might want a non-destructive way to iterate over something asynchronously.<a href="#fnref:intoasynciterator" class="footnote-backref">↩</a></p>
</div>
<div class="footnote-definition" id="multi-future"><sup class="footnote-definition-label">3</sup>
<p>If we go this route, I'd suggest having <code>AsyncIterator::next</code> return a subtrait of <code>Future</code> called something like <code>IterableFuture</code> or <code>MultiCompletionFuture</code> to highlight multi-completion semantics. This is similar to how <a href="https://docs.rs/futures/latest/futures/future/trait.FusedFuture.html"><code>FusedFuture</code></a> adds additional semantics to the underlying <code>Future</code> trait.<a href="#fnref:multi-future" class="footnote-backref">↩</a></p>
</div>
]]></content><author><name>Eric Holk</name></author><summary type="html"><![CDATA[<p>There are currently two competing designs for async iteration traits for Rust.
The first is <code>poll_next</code>.
The second is <code>async fn next</code>.
I see strengths to each design.
The <code>poll_next</code> design seems stronger on technical concerns, such as performance and ease of implementation.
The <code>async fn next</code> design seems better from an ergonomics and consistency perspective.
Unfortunately, the process of resolve this debate has been slow going.</p>
<p>One thing I've realized in the debate is that at this point the two designs are more similar than they may seem at first glance.
In this post I'd like to show how a handful of minor tweaks to each design results in something that is the same modulo names.</p>
]]></summary></entry><entry><title type="html">Async Cancellation and Panic</title><link href="https://theincredibleholk.org/blog/2024/03/06/async-cancellation-and-panic/" rel="alternate" type="text/html" title="Eric Holk"/><published>2024-03-06T00:00:00+00:00</published><updated>2024-03-06T00:00:00+00:00</updated><id>https://theincredibleholk.org/blog/2024/03/06/async-cancellation-and-panic/</id><content type="html" xml:base="https://theincredibleholk.org/blog/2024/03/06/async-cancellation-and-panic/"><![CDATA[<p>When I last wrote about async cancellation in Rust, I touched briefly on the question of how cancellation interacts with panic.
Mostly I left it as <a href="/blog/2023/11/14/a-mechanism-for-async-cancellation/#cancel-during-unwind">an exercise for the reader</a> and left a rough sketch for how I thought it would work.
More recently, Boats <a href="https://without.boats/blog/asynchronous-clean-up/#async-unwinding">touched on the issue</a> in a little more detail, but I think there are still a lot of open questions.
In this post, I'd like to experiment with unwinding using my <a href="https://github.com/eholk/explicit-async-cancellation">cancellation prototype</a> and build on some of the previous work in this area.</p>
<!-- MORE -->
<h1 id="it-s-not-as-easy-as-i-thought">It's not as easy as I thought<a class="header-anchor" href="#it-s-not-as-easy-as-i-thought">🔗</a></h1>
<p>In the <a href="/blog/2023/11/14/a-mechanism-for-async-cancellation/#cancel-during-unwind">sketch I laid out before</a>, I expected the core idea of supporting cancellation during unwinding would be to have the executor, and any mini-executors like <code>race</code> and <code>join</code>, would basically wrap calls to <code>poll</code> with <code>catch_unwind</code>, then in the <code>Err</code> case, call <code>poll_cancel</code> to completion and then call <code>resume_unwind</code>.
In pseudo-code, that would look something like:</p>
<pre style="background-color:#ffffff;">
<span style="font-weight:bold;color:#a71d5d;">loop </span><span style="color:#323232;">{
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">match </span><span style="color:#62a35c;">catch_unwind</span><span style="color:#323232;">(|| task.</span><span style="color:#62a35c;">poll</span><span style="color:#323232;">(cx)) {
</span><span style="color:#323232;">        </span><span style="color:#0086b3;">Ok</span><span style="color:#323232;">(Poll::Ready(x)) </span><span style="font-weight:bold;color:#a71d5d;">=&gt; return</span><span style="color:#323232;"> x,
</span><span style="color:#323232;">        </span><span style="color:#0086b3;">Ok</span><span style="color:#323232;">(Poll::Pending) </span><span style="font-weight:bold;color:#a71d5d;">=&gt; continue</span><span style="color:#323232;">,
</span><span style="color:#323232;">        </span><span style="color:#0086b3;">Err</span><span style="color:#323232;">(panic) </span><span style="font-weight:bold;color:#a71d5d;">=&gt; </span><span style="color:#323232;">{
</span><span style="color:#323232;">            </span><span style="font-weight:bold;color:#a71d5d;">while </span><span style="color:#323232;">Poll::Pending </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> task.</span><span style="color:#62a35c;">poll_cancel</span><span style="color:#323232;">(cx) {}
</span><span style="color:#323232;">            </span><span style="color:#62a35c;">resume_unwind</span><span style="color:#323232;">(panic);
</span><span style="color:#323232;">        }
</span><span style="color:#323232;">    }
</span><span style="color:#323232;">}
</span></pre>
<p>Unfortunately this doesn't work.
It turns out I had some inkling this might be the case when I wrote:</p>
<blockquote>
<p>There are other challenges though.
One is that the <code>poll_cancel</code> functions will need to be written to be aware of the fact that they might be called during unwinding, which means the internal state for the future might be inconsistent.</p>
</blockquote>
<p>To understand what's wrong, recall that I desugared cancellation-aware <code>async</code> blocks into coroutines.
Rust coroutines only have one entry point, which is the <code>resume</code> method.
I simulated two entry points (<code>poll</code> and <code>poll_cancel</code>) by passing another argument into <code>resume</code>.
The thing is, once <code>resume</code> panics, coroutines cannot be resumed again and they will panic if you try.
Since <code>poll</code> and <code>poll_cancel</code> are backed by the same <code>resume</code> method, this means we can't call <code>poll_cancel</code> after <code>poll</code> panics.</p>
<p>Some of this is an artifact of the way this experiment is structured.
If we had proper compiler support for multiple entry points to a coroutine, we might be able to make this work.
But I think it's more composable and more in line with existing precedent to follow a rule where all unwinding or cancellation work needs to finish before a panic leaves the <code>poll</code> call.</p>
<h1 id="an-approach-that-actually-works">An approach that actually works<a class="header-anchor" href="#an-approach-that-actually-works">🔗</a></h1>
<p>This realization that we need to process and cancellations before unwinding out of <code>poll</code> felt constraining at first, but it actually simplifies a lot of the design.
I thought we'd need to wrap basically every call to <code>poll</code> in <code>catch_unwind</code>, but in most cases this is unnecessary and we can instead let the usual unwinding machinery proceed as normal.
The places where we do care are when we know of multiple futures and if one of them panics we need to cancel the rest.</p>
<p>Let's do <code>on_cancel</code> as an example.
While I don't think <code>on_cancel</code> would be a great API to support in production, it is useful to focus on the specifics of cancellation behavior.</p>
<p>In the <a href="/blog/2023/11/14/a-mechanism-for-async-cancellation/">last post</a>, I was thinking of <code>on_cancel</code> almost as an approximation of an exception handler.
For our purposes today, I think it's more useful to think of it as a kind of future combinator.
In this view, <code>on_cancel</code> produces a new future from two others, one that is the normal execution path, and another future that is run only when the future is cancelled.<sup class="footnote-reference"><a href="#finally" id="fnref:finally">1</a></sup></p>
<p>Looking at it this way, we can see what we should do when the <code>poll</code> function on the main future panics.
We aren't allowed to poll the future that's panicking anymore, because its internal state might be inconsistent.
We have to trust that as <code>poll</code> was unwinding, the future ran any cancellation handlers that were on the stack.
But, since we want cancel-on-unwind semantics, the <code>on_cancel</code> combinator needs to catch the panic, run the cancellation future to completion, and then resume unwinding.</p>
<h2 id="deriving-the-implementation">Deriving the implementation<a class="header-anchor" href="#deriving-the-implementation">🔗</a></h2>
<p>Now let's see how to add cancellation on panic behavior to our existing <code>on_cancel</code> implementation.
My <a href="/blog/2023/11/14/a-mechanism-for-async-cancellation/">last post</a> didn't really go into the details on this, so let's start with a rough sketch of the previous <code>on_cancel</code> implementation.
Throughout this section I'm going to ignore details like pinning and <code>unsafe</code> so we can focus on the main idea.
I have a complete working implementation of the ideas in this section available at <a href="https://github.com/eholk/explicit-async-cancellation">https://github.com/eholk/explicit-async-cancellation</a>.</p>
<p>The <code>on_cancel</code> method returns a future that's carries a cancellation handler.
While the details are hidden in the surface API, the struct and future implementation returned looks like this:</p>
<pre style="background-color:#ffffff;">
<span style="font-weight:bold;color:#a71d5d;">struct&lt;</span><span style="color:#323232;">F, H</span><span style="font-weight:bold;color:#a71d5d;">&gt;</span><span style="color:#323232;"> OnCancel {
</span><span style="color:#323232;">    future: F,
</span><span style="color:#323232;">    on_cancel: Option&lt;H&gt;,
</span><span style="color:#323232;">}
</span><span style="color:#323232;">
</span><span style="font-weight:bold;color:#a71d5d;">impl</span><span style="color:#323232;">&lt;F, H&gt; Future </span><span style="font-weight:bold;color:#a71d5d;">for </span><span style="color:#323232;">OnCancel&lt;F, H&gt;
</span><span style="font-weight:bold;color:#a71d5d;">where
</span><span style="color:#323232;">    F: Future,
</span><span style="color:#323232;">    H: Future,
</span><span style="color:#323232;">{
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">type </span><span style="color:#323232;">Output </span><span style="font-weight:bold;color:#a71d5d;">= </span><span style="color:#323232;">F::Output;
</span><span style="color:#323232;">
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">fn </span><span style="font-weight:bold;color:#795da3;">poll</span><span style="color:#323232;">(self: Pin&lt;</span><span style="font-weight:bold;color:#a71d5d;">&amp;mut Self</span><span style="color:#323232;">&gt;, cx: </span><span style="font-weight:bold;color:#a71d5d;">&amp;mut</span><span style="color:#323232;"> Context) -&gt; Poll&lt;</span><span style="font-weight:bold;color:#a71d5d;">Self::</span><span style="color:#323232;">Output&gt; {
</span><span style="color:#323232;">        self.future.</span><span style="color:#62a35c;">poll</span><span style="color:#323232;">(cx)
</span><span style="color:#323232;">    }
</span><span style="color:#323232;">
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">fn </span><span style="font-weight:bold;color:#795da3;">poll_cancel</span><span style="color:#323232;">(self: Pin&lt;</span><span style="font-weight:bold;color:#a71d5d;">&amp;mut Self</span><span style="color:#323232;">&gt;, cx: </span><span style="font-weight:bold;color:#a71d5d;">&amp;mut</span><span style="color:#323232;"> Context) -&gt; Poll&lt;()&gt; {
</span><span style="color:#323232;">        </span><span style="font-style:italic;color:#969896;">// run the cancellation handler if it&#39;s still present
</span><span style="color:#323232;">        </span><span style="font-weight:bold;color:#a71d5d;">if let </span><span style="color:#0086b3;">Some</span><span style="color:#323232;">(on_cancel) </span><span style="font-weight:bold;color:#a71d5d;">= </span><span style="color:#323232;">self.on_cancel {
</span><span style="color:#323232;">            </span><span style="font-weight:bold;color:#a71d5d;">match</span><span style="color:#323232;"> on_cancel.</span><span style="color:#62a35c;">poll</span><span style="color:#323232;">(cx) {
</span><span style="color:#323232;">                </span><span style="font-style:italic;color:#969896;">// if cancellation is complete, clear the handler so we won&#39;t try to run it again
</span><span style="color:#323232;">                Poll::Ready(()) </span><span style="font-weight:bold;color:#a71d5d;">=&gt; </span><span style="color:#323232;">self.on_cancel </span><span style="font-weight:bold;color:#a71d5d;">= </span><span style="color:#0086b3;">None</span><span style="color:#323232;">,
</span><span style="color:#323232;">                </span><span style="font-style:italic;color:#969896;">// cancellation is not finished, so yield to the caller.
</span><span style="color:#323232;">                Poll::Pending </span><span style="font-weight:bold;color:#a71d5d;">=&gt; return </span><span style="color:#323232;">Poll::Pending,
</span><span style="color:#323232;">            }
</span><span style="color:#323232;">        }
</span><span style="color:#323232;">
</span><span style="color:#323232;">        </span><span style="font-style:italic;color:#969896;">// run any cancellation handlers on the inner future
</span><span style="color:#323232;">        self.future.</span><span style="color:#62a35c;">poll_cancel</span><span style="color:#323232;">(cx)
</span><span style="color:#323232;">    }
</span><span style="color:#323232;">}
</span></pre>
<p>The <code>poll</code> function is pretty uninteresting.
We just forward it to the inner future.
The <code>poll_cancel</code> function is a little more subtle.
The main thing we need to do is run the cancellation handler, which we do by calling <code>poll</code> on it.
However, the inner future might also have nested cancellation handlers, so we need to call <code>poll_cancel</code> on it as well.
This is also why I chose to wrap the cancellation hook in an <code>Option</code>, since I can use that as a flag to indicate whether the cancellation hook is finished.</p>
<p>As an aside, I chose to do outside-in cancellation semantics here since drop also runs outside-in.
I'm not sure this was the right choice.
For example, unwinding is inside-out instead.
I think it's worth thinking harder about what the right ordering is, but for now it's easy to change and independent of our focus today.</p>
<p>Okay, so now that we have a basic <code>on_cancel</code> implementation, let's handle what happens if the call to the nested future's <code>poll</code> panics.
In short, we need to wrap the call to <code>poll</code> in <code>catch_unwind</code>.</p>
<pre style="background-color:#ffffff;">
<span style="font-weight:bold;color:#a71d5d;">fn </span><span style="font-weight:bold;color:#795da3;">poll</span><span style="color:#323232;">(self: Pin&lt;</span><span style="font-weight:bold;color:#a71d5d;">&amp;mut Self</span><span style="color:#323232;">&gt;, cx: </span><span style="font-weight:bold;color:#a71d5d;">&amp;mut</span><span style="color:#323232;"> Context) -&gt; Poll&lt;</span><span style="font-weight:bold;color:#a71d5d;">Self::</span><span style="color:#323232;">Output&gt; {
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">match </span><span style="color:#62a35c;">catch_unwind</span><span style="color:#323232;">(|| self.future.</span><span style="color:#62a35c;">poll</span><span style="color:#323232;">(cx)) {
</span><span style="color:#323232;">        </span><span style="color:#0086b3;">Ok</span><span style="color:#323232;">(poll) </span><span style="font-weight:bold;color:#a71d5d;">=&gt;</span><span style="color:#323232;"> poll,
</span><span style="color:#323232;">        </span><span style="color:#0086b3;">Err</span><span style="color:#323232;">(panic) </span><span style="font-weight:bold;color:#a71d5d;">=&gt; </span><span style="color:#323232;">todo!(</span><span style="color:#183691;">&quot;run the cancellation hook at then resume unwinding&quot;</span><span style="color:#323232;">),
</span><span style="color:#323232;">    }
</span><span style="color:#323232;">}
</span></pre>
<p>Now let's think about the <code>Err</code> case.
Basically, we need to cancel ourselves, which we can do by calling <code>poll_cancel</code>.
Then we need to resume unwinding.
Because <code>poll_cancel</code> might take several tries to finish, we need to save the panic information so we can resume unwinding after it's done.
So we'll add another field to <code>OnCancel</code> to optionally store the panic information.</p>
<pre style="background-color:#ffffff;">
<span style="font-weight:bold;color:#a71d5d;">struct&lt;</span><span style="color:#323232;">F, H</span><span style="font-weight:bold;color:#a71d5d;">&gt;</span><span style="color:#323232;"> OnCancel {
</span><span style="color:#323232;">    future: F,
</span><span style="color:#323232;">    on_cancel: Option&lt;H&gt;,
</span><span style="color:#323232;">    panic: Option&lt;Box&lt;dyn Any </span><span style="font-weight:bold;color:#a71d5d;">+</span><span style="color:#323232;"> Send </span><span style="font-weight:bold;color:#a71d5d;">+ &#39;static</span><span style="color:#323232;">&gt;&gt;,
</span><span style="color:#323232;">}
</span><span style="color:#323232;">
</span><span style="font-weight:bold;color:#a71d5d;">impl</span><span style="color:#323232;">&lt;F, H&gt; Future </span><span style="font-weight:bold;color:#a71d5d;">for </span><span style="color:#323232;">OnCancel&lt;F, H&gt;
</span><span style="font-weight:bold;color:#a71d5d;">where
</span><span style="color:#323232;">    F: Future,
</span><span style="color:#323232;">    H: Future,
</span><span style="color:#323232;">{
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">type </span><span style="color:#323232;">Output </span><span style="font-weight:bold;color:#a71d5d;">= </span><span style="color:#323232;">F::Output;
</span><span style="color:#323232;">
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">fn </span><span style="font-weight:bold;color:#795da3;">poll</span><span style="color:#323232;">(self: Pin&lt;</span><span style="font-weight:bold;color:#a71d5d;">&amp;mut Self</span><span style="color:#323232;">&gt;, cx: </span><span style="font-weight:bold;color:#a71d5d;">&amp;mut</span><span style="color:#323232;"> Context) -&gt; Poll&lt;</span><span style="font-weight:bold;color:#a71d5d;">Self::</span><span style="color:#323232;">Output&gt; {
</span><span style="color:#323232;">        </span><span style="font-weight:bold;color:#a71d5d;">match </span><span style="color:#62a35c;">catch_unwind</span><span style="color:#323232;">(|| self.future.</span><span style="color:#62a35c;">poll</span><span style="color:#323232;">(cx)) {
</span><span style="color:#323232;">            </span><span style="color:#0086b3;">Ok</span><span style="color:#323232;">(poll) </span><span style="font-weight:bold;color:#a71d5d;">=&gt;</span><span style="color:#323232;"> poll,
</span><span style="color:#323232;">            </span><span style="color:#0086b3;">Err</span><span style="color:#323232;">(panic) </span><span style="font-weight:bold;color:#a71d5d;">=&gt; </span><span style="color:#323232;">{
</span><span style="color:#323232;">                self.panic </span><span style="font-weight:bold;color:#a71d5d;">= </span><span style="color:#0086b3;">Some</span><span style="color:#323232;">(panic);
</span><span style="color:#323232;">                </span><span style="font-weight:bold;color:#a71d5d;">match </span><span style="color:#323232;">self.</span><span style="color:#62a35c;">poll_cancel</span><span style="color:#323232;">(cx) {
</span><span style="color:#323232;">                    Poll::Ready(()) </span><span style="font-weight:bold;color:#a71d5d;">=&gt; </span><span style="color:#62a35c;">resume_unwind</span><span style="color:#323232;">(self.panic.</span><span style="color:#62a35c;">take</span><span style="color:#323232;">().</span><span style="color:#62a35c;">unwrap</span><span style="color:#323232;">()),
</span><span style="color:#323232;">                    Poll::Pending </span><span style="font-weight:bold;color:#a71d5d;">=&gt; </span><span style="color:#323232;">Poll::Pending,
</span><span style="color:#323232;">                }
</span><span style="color:#323232;">            },
</span><span style="color:#323232;">        }
</span><span style="color:#323232;">    }
</span><span style="color:#323232;">
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">fn </span><span style="font-weight:bold;color:#795da3;">poll_cancel</span><span style="color:#323232;">(self: Pin&lt;</span><span style="font-weight:bold;color:#a71d5d;">&amp;mut Self</span><span style="color:#323232;">&gt;, cx: </span><span style="font-weight:bold;color:#a71d5d;">&amp;mut</span><span style="color:#323232;"> Context) -&gt; Poll&lt;()&gt; {
</span><span style="color:#323232;">        todo!(</span><span style="color:#183691;">&quot;we&#39;ll come back to this in a minute&quot;</span><span style="color:#323232;">)
</span><span style="color:#323232;">    }
</span><span style="color:#323232;">}
</span></pre>
<p>We're part of the way there, but we still have some problems.
Assuming <code>poll_cancel</code> were correct (it's not, but we'll get there), we'd be okay if cancellation finished promptly.
But if not, it will return <code>Pending</code>, which we'll bubble up to the caller.
The caller doesn't know we're panicking, since we've hidden the panic information away in our <code>panic</code> field, so it will eventually call <code>poll</code> on us again.
Unfortunately, this means we'll poll the inner future, which we've previously said is not allowed.
So we need to make a small change to check if we're in the process of panicking when we're polled.</p>
<pre style="background-color:#ffffff;">
<span style="font-weight:bold;color:#a71d5d;">fn </span><span style="font-weight:bold;color:#795da3;">poll</span><span style="color:#323232;">(self: Pin&lt;</span><span style="font-weight:bold;color:#a71d5d;">&amp;mut Self</span><span style="color:#323232;">&gt;, cx: </span><span style="font-weight:bold;color:#a71d5d;">&amp;mut</span><span style="color:#323232;"> Context) -&gt; Poll&lt;</span><span style="font-weight:bold;color:#a71d5d;">Self::</span><span style="color:#323232;">Output&gt; {
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">if </span><span style="color:#323232;">self.panic.</span><span style="color:#62a35c;">is_some</span><span style="color:#323232;">() {
</span><span style="color:#323232;">        </span><span style="font-weight:bold;color:#a71d5d;">match </span><span style="color:#323232;">self.</span><span style="color:#62a35c;">poll_cancel</span><span style="color:#323232;">(cx) {
</span><span style="color:#323232;">            Poll::Ready(()) </span><span style="font-weight:bold;color:#a71d5d;">=&gt; </span><span style="color:#62a35c;">resume_unwind</span><span style="color:#323232;">(self.panic.</span><span style="color:#62a35c;">take</span><span style="color:#323232;">().</span><span style="color:#62a35c;">unwrap</span><span style="color:#323232;">()),
</span><span style="color:#323232;">            Poll::Pending </span><span style="font-weight:bold;color:#a71d5d;">=&gt; return </span><span style="color:#323232;">Poll::Pending,
</span><span style="color:#323232;">        }
</span><span style="color:#323232;">    }
</span><span style="color:#323232;">
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">match </span><span style="color:#62a35c;">catch_unwind</span><span style="color:#323232;">(|| self.future.</span><span style="color:#62a35c;">poll</span><span style="color:#323232;">(cx)) {
</span><span style="color:#323232;">        </span><span style="color:#0086b3;">Ok</span><span style="color:#323232;">(poll) </span><span style="font-weight:bold;color:#a71d5d;">=&gt;</span><span style="color:#323232;"> poll,
</span><span style="color:#323232;">        </span><span style="color:#0086b3;">Err</span><span style="color:#323232;">(panic) </span><span style="font-weight:bold;color:#a71d5d;">=&gt; </span><span style="color:#323232;">{
</span><span style="color:#323232;">            self.panic </span><span style="font-weight:bold;color:#a71d5d;">= </span><span style="color:#0086b3;">Some</span><span style="color:#323232;">(panic);
</span><span style="color:#323232;">            </span><span style="font-weight:bold;color:#a71d5d;">match </span><span style="color:#323232;">self.</span><span style="color:#62a35c;">poll_cancel</span><span style="color:#323232;">(cx) {
</span><span style="color:#323232;">                Poll::Ready(()) </span><span style="font-weight:bold;color:#a71d5d;">=&gt; </span><span style="color:#62a35c;">resume_unwind</span><span style="color:#323232;">(self.panic.</span><span style="color:#62a35c;">take</span><span style="color:#323232;">().</span><span style="color:#62a35c;">unwrap</span><span style="color:#323232;">()),
</span><span style="color:#323232;">                Poll::Pending </span><span style="font-weight:bold;color:#a71d5d;">=&gt; </span><span style="color:#323232;">Poll::Pending,
</span><span style="color:#323232;">            }
</span><span style="color:#323232;">        },
</span><span style="color:#323232;">    }
</span><span style="color:#323232;">}
</span></pre>
<p>And now we're all set.
If we're polled when there's panic information present then we never get to the call to <code>self.future.poll(cx)</code>.</p>
<p>Now it's time to revisit <code>poll_cancel</code>.
To share some logic, I had the panic path in <code>poll</code> call into <code>poll_cancel</code>, but this means we need to update <code>poll_cancel</code> to recognize that it can be called while panicking.
Here's how:</p>
<pre style="background-color:#ffffff;">
<span style="font-weight:bold;color:#a71d5d;">fn </span><span style="font-weight:bold;color:#795da3;">poll_cancel</span><span style="color:#323232;">(self: Pin&lt;</span><span style="font-weight:bold;color:#a71d5d;">&amp;mut Self</span><span style="color:#323232;">&gt;, cx: </span><span style="font-weight:bold;color:#a71d5d;">&amp;mut</span><span style="color:#323232;"> Context) -&gt; Poll&lt;()&gt; {
</span><span style="color:#323232;">    </span><span style="font-style:italic;color:#969896;">// run the cancellation handler if it&#39;s still present (this part stays the same)
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">if let </span><span style="color:#0086b3;">Some</span><span style="color:#323232;">(on_cancel) </span><span style="font-weight:bold;color:#a71d5d;">= </span><span style="color:#323232;">self.on_cancel {
</span><span style="color:#323232;">        </span><span style="font-weight:bold;color:#a71d5d;">match</span><span style="color:#323232;"> on_cancel.</span><span style="color:#62a35c;">poll</span><span style="color:#323232;">(cx) {
</span><span style="color:#323232;">            </span><span style="font-style:italic;color:#969896;">// if cancellation is complete, clear the handler so we won&#39;t try to run it again
</span><span style="color:#323232;">            Poll::Ready(()) </span><span style="font-weight:bold;color:#a71d5d;">=&gt; </span><span style="color:#323232;">self.on_cancel </span><span style="font-weight:bold;color:#a71d5d;">= </span><span style="color:#0086b3;">None</span><span style="color:#323232;">,
</span><span style="color:#323232;">            </span><span style="font-style:italic;color:#969896;">// cancellation is not finished, so yield to the caller.
</span><span style="color:#323232;">            Poll::Pending </span><span style="font-weight:bold;color:#a71d5d;">=&gt; return </span><span style="color:#323232;">Poll::Pending,
</span><span style="color:#323232;">        }
</span><span style="color:#323232;">    }
</span><span style="color:#323232;">
</span><span style="color:#323232;">    </span><span style="font-style:italic;color:#969896;">// if we aren&#39;t panicking, run any cancellation handlers on the inner future
</span><span style="color:#323232;">    </span><span style="font-style:italic;color:#969896;">// otherwise, resume unwinding
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">match </span><span style="color:#323232;">self.panic {
</span><span style="color:#323232;">        </span><span style="color:#0086b3;">None </span><span style="font-weight:bold;color:#a71d5d;">=&gt; </span><span style="color:#323232;">self.future.</span><span style="color:#62a35c;">poll_cancel</span><span style="color:#323232;">(cx)
</span><span style="color:#323232;">        </span><span style="color:#0086b3;">Some</span><span style="color:#323232;">(</span><span style="font-weight:bold;color:#a71d5d;">_</span><span style="color:#323232;">) </span><span style="font-weight:bold;color:#a71d5d;">=&gt; </span><span style="color:#62a35c;">resume_unwind</span><span style="color:#323232;">(self.panic.</span><span style="color:#62a35c;">take</span><span style="color:#323232;">().</span><span style="color:#62a35c;">unwrap</span><span style="color:#323232;">()),
</span><span style="color:#323232;">    }
</span><span style="color:#323232;">}
</span></pre>
<p>The first part, where we run the cancellation hook, stays the same as before.
In the second part, we would normally cancel the inner future, but remember that if we are panicking we aren't allowed to poll it again.</p>
<p>It's worth asking what we should do in the <code>Some</code> line though.
At this point we know we are in the process of unwinding, and all cleanup code has finished.
One option is to return <code>Poll::Ready(())</code> here, and if we're called from <code>poll</code> then we could count on it calling <code>resume_unwind</code>.
However, it could also be that while we were waiting on the cancellation to finish, the executor decided to cancel us.
In this case, if we returned <code>Poll::Ready(())</code> then we would swallow the exception.
So instead, the right answer is to <code>resume_unwind</code> here as well.</p>
<p>So there we have it: how to cancel a future when polling it panics.</p>
<h1 id="should-we-do-this">Should we do this?<a class="header-anchor" href="#should-we-do-this">🔗</a></h1>
<div style="float: left; width: 33%; margin-right: 1.5em">
<p><img src="/images/malcolm.png" alt="Your scientists were so preoccupied with whether or not they could, they didn&#39;t stop to think if they should." /></p>
</div>
<p>We've shown that it's at least somewhat possible to support async cleanup code while unwinding.
I'll admit, beyond a basic smoke test, I haven't really probed the limits of this design.
For example, what happens if we panic while running the cancellation handler as a result of another panic?
Or what actually happens if the executor cancels us while we are cleaning up before resuming a panic?
If we were to RFC something like this, these are all questions that we'd need to explore.</p>
<p>The reason I decided to go ahead and write this post without answering those questions is that in this post I think we've already learned enough that we can start evaluating this design and inform future options.</p>
<p>First of all, something about suspending while in the process of unwinding just feels fundamentally weird and uncomfortable.
That said, I think we can develop a reasonable semantics for this behavior if we decide we want it.</p>
<p>But this also leads to a shortcoming that I'm not sure how to resolve.
This prototype cannot work in <code>#[no_std]</code> environments, because <code>catch_unwind</code> and <code>resume_unwind</code> represent panic information as a <code>Box&lt;dyn Any + Send + 'static&gt;</code>, meaning we need an allocator.
This is a non-starter for something that we'd want to consider building in as a core Rust language feature.
The whole <code>async</code>/<code>await</code> system has been carefully designed not to need an allocator, and we need to preserve this property.
After all, <code>async</code>/<code>await</code> has found a lot of success in microcontroller environments!</p>
<p>Is this necessary though?
Or is it an artifact of trying to prototype a system purely in library code without compiler support?
As an analogy, we could imagine prototyping destructors using <code>catch_unwind</code>, but rustc is able to generate code to run destructors during unwinding without needing to reify the exception.</p>
<p>Unfortunately I don't think we can avoid the issue in the same way.
The problem is that normal unwinding doesn't suspend the execution at all, while we very much need to be able to do that to <code>await</code> in the unwinding path.
This means the exception does need to be stored somewhere (presumably with the future), and we need to be able to resume unwinding later.
If you're using a work-stealing executor, this means it's even possible that your task could start unwinding on one thread and finish on another.
So we need somewhere to store the exception that's not ephemeral in the way that it is during the Rust-generated unwind code.</p>
<p>There might be other options that could work.
For example, the executor could reserve some space for each task that's large enough to hold most panics.
Most likely the way we'd accomplish this is by attaching something to the <code>Context</code> that gives access to it.
Maybe it'd be specific to panics, or maybe it'd be a more general task-local bump allocator or something like that.
At any rate, we could add API surface for a minimal allocator to support awaiting while unwinding without needing a full-blown global allocator.
These could be made optional, which would give executors the option of aborting if they cannot or don't want to support async unwinding.</p>
<p>Another option would be to have the compiler not automatically generate calls to <code>poll_cancel</code> while unwinding, and instead provide something like an async version of <code>catch_unwind</code>.
I think something like this is <a href="https://without.boats/blog/asynchronous-clean-up/#async-unwinding">what boats was proposing</a>.
The nice thing about this option is that we can completely give up on supporting <code>#[no_std]</code>.
Furthermore, we don't have to worry about being "zero cost," since the fact that the user called <code>async_catch_unwind</code> signals that they're willing to pay the cost that's needed.</p>
<p>That said, it's not clear how that should interact with <code>do ... final</code> blocks if we were to add them.<sup class="footnote-reference"><a href="#do-final" id="fnref:do-final">2</a></sup>
For example, the <code>final</code> block would presumably run during unwinding in sync code, so it seems like we'd also need to do it while unwinding in async code.
Unfortunately, as far as I can tell that will run into the same allocation problems.</p>
<p>So to go back to the question of whether we should do this, I think we need more exploration.
There are some options, but from my exploration here it seems like it's hard to satisfy all our requirements.
But maybe one of these, or some other option, can strike a decent compromise.</p>
<hr />
<div class="footnote-definition" id="finally"><sup class="footnote-definition-label">1</sup>
<p>With a small tweak, we could approximate a <code>finally</code> clause, by making it so we run the cancellation future even if the main future completes successfully.<a href="#fnref:finally" class="footnote-backref">↩</a></p>
</div>
<div class="footnote-definition" id="do-final"><sup class="footnote-definition-label">2</sup>
<p>I really like the idea of <code>do ... final</code>! I had hoped to explore that some in this post but I felt there was enough material here without it.<a href="#fnref:do-final" class="footnote-backref">↩</a></p>
</div>
]]></content><author><name>Eric Holk</name></author><summary type="html"><![CDATA[<p>When I last wrote about async cancellation in Rust, I touched briefly on the question of how cancellation interacts with panic.
Mostly I left it as <a href="/blog/2023/11/14/a-mechanism-for-async-cancellation/#cancel-during-unwind">an exercise for the reader</a> and left a rough sketch for how I thought it would work.
More recently, Boats <a href="https://without.boats/blog/asynchronous-clean-up/#async-unwinding">touched on the issue</a> in a little more detail, but I think there are still a lot of open questions.
In this post, I'd like to experiment with unwinding using my <a href="https://github.com/eholk/explicit-async-cancellation">cancellation prototype</a> and build on some of the previous work in this area.</p>
]]></summary></entry><entry><title type="html">How to Shrink Rust</title><link href="https://theincredibleholk.org/blog/2024/01/08/how-to-shrink-rust/" rel="alternate" type="text/html" title="Eric Holk"/><published>2024-01-08T00:00:00+00:00</published><updated>2024-01-08T00:00:00+00:00</updated><id>https://theincredibleholk.org/blog/2024/01/08/how-to-shrink-rust/</id><content type="html" xml:base="https://theincredibleholk.org/blog/2024/01/08/how-to-shrink-rust/"><![CDATA[<p>While doing some housekeeping on my blog over the weekend, I can across an
<a href="https://web.archive.org/web/20120812052624/http://pcwalton.github.com/blog/2012/06/03/maximally-minimal-classes-for-rust/">ancient post</a> by Patrick Walton.
While I didn't realize it at the time, this post embodies what has become one of my core principles in program language design.[^spiky-blob]
In re-reading Patrick's post, this quote stood out in particular:</p>
<blockquote>
<p>Language design tends to go in cycles: we grow the language to accommodate new
functionality, then shrink the language as we discover ways in which the
features can be orthogonally integrated into the rest of the system. Classes
seem to me to be on the upward trajectory of complexity; now it’s time to
shrink them down. At the same time, we shouldn’t sacrifice the functionality
that they enable.</p>
</blockquote>
<p>This cycle of growing and shrinking as a key part of the process in the early days of Rust.
Upon reading this section, I found myself asking "how could we shrink Rust today?"</p>
<!-- MORE -->
<h1 id="what-happened-to-classes">What happened to classes?<a class="header-anchor" href="#what-happened-to-classes">🔗</a></h1>
<p>To be honest, I had forgotten Rust had <code>classes</code> at one point.
I remembered resources and objects, but forgot we had a brief window where there were classes.
Patrick's <a href="https://web.archive.org/web/20120812052624/http://pcwalton.github.com/blog/2012/06/03/maximally-minimal-classes-for-rust/">post</a> explains what happened to them.
Essentially, once we added classes and a bunch of other features, we realized that classes combined five features that we could implement independently in a way that's more general.
These, along with their modern replacements in Rust, are:</p>
<ol>
<li>Nominal records, replaced by <code>struct</code>.</li>
<li>Constructors, replaced by <code>struct</code> literal syntax and plain functions that are conventionally called <code>new</code>.</li>
<li>Field-level privacy, replaced by module-level privacy.<sup class="footnote-reference"><a href="#namespace" id="fnref:namespace">1</a></sup></li>
<li>Attached methods, replaced by inherent <code>impl</code>s.</li>
<li>Destructors, replaced by <code>Drop</code> trait.</li>
</ol>
<p>Some of these features weren't so much replaced as removed.
For example, it's hard to claim Rust has constructors today, other than by convention.
Similarly, if I remember right, at the time Rust <em>also</em> had the <code>struct</code> keyword, so you used <code>struct</code> if you just wanted a nominal record or <code>class</code> if you wanted the rest of these features.
Or in the case of field-level privacy, we basically just decided this feature wasn't necessary.<sup class="footnote-reference"><a href="#privacy" id="fnref:privacy">2</a></sup></p>
<p>For the two features that had a clear replacement, by decoupling them from classes we gained a lot more power.
You can attach methods to any type now, like <code>enum</code>s and even primitive types, not just classes.
Destructors are much simpler now too, since you implement <code>Drop</code> just like any other trait.<sup class="footnote-reference"><a href="#drop" id="fnref:drop">3</a></sup></p>
<p>The end result of this was we replaced a large feature, classes, with a handful of smaller, orthogonal features.
The result was something that composed better<sup class="footnote-reference"><a href="#class-enum" id="fnref:class-enum">4</a></sup> and gave us more power and flexibility.</p>
<h1 id="what-does-this-have-to-do-with-rust-today">What does this have to do with Rust today?<a class="header-anchor" href="#what-does-this-have-to-do-with-rust-today">🔗</a></h1>
<p>To me the key take away, at least looking back from over a decade later, is that
a big part of why Rust is the way it is today is that we were able to add a
bunch of features and then pare them down once we got some experience. In Rust's
history, it's had three different ways to do destructors, and while I don't
recall exactly, I suspect at least two of these coexisted at some point.</p>
<p>It's somewhat harder to follow this model now. In the early days, we made
breaking syntax changes sometimes multiple times a week.<sup class="footnote-reference"><a href="#rustfmt" id="fnref:rustfmt">5</a></sup> At that time,
the Rust team was a handful of people, about as many interns, and some people
who hung out on IRC. Today the community is much larger and people are using
Rust in mission-critical projects where they can't afford to make weekly syntax
updates. And of course, Rust 1.0 came with a promise that there would be no more
breaking changes. You can can rely on Rust to keep working tomorrow.</p>
<p>Rust is still able to grow, but shrinking is much harder, and as a result, we
have to be much more conservative in how Rust grows. We have some ability to
shrink through the editions system, but this is still not a great mechanism for
rapidly iterating on designs.</p>
<p>Anyway, I don't really have a solution, or even necessarily a clearly defined
problem. I mostly just wanted to observe that developing Rust is harder today
because we mostly have to look at things incrementally. It's much harder to
design a set of interrelated features that maybe by themselves wouldn't be
particularly noteworthy but together are quite powerful.</p>
<p>Fortunately, Rust does have the nightly compiler, and a process for experiments.
That seems like the right environment to do the kind of language experimentation
today that was possible in the early days. This is the same codebase that
becomes the stable compiler, so we still need to emphasize stability and
maintainability, but liberal experimentation in the nightly compiler with many
different Rust features at once seems like it has the possibility to do the same
kind of broad scale language iteration that we did in the early days while
staying true to Rust's stability promises.</p>
<hr />
<div class="footnote-definition" id="spiky-blog"><sup class="footnote-definition-label">1</sup>
<p>I've since started calling this my Spiky Blog Theory of Programming Languages, but it deserves a post of its own.<a href="#fnref:spiky-blog" class="footnote-backref">↩</a></p>
</div>
<div class="footnote-definition" id="namespace"><sup class="footnote-definition-label">2</sup>
<p>One way of looking at this is that classes included their own module or namespace, and this was seen as unnecessary complexity.<a href="#fnref:namespace" class="footnote-backref">↩</a></p>
</div>
<div class="footnote-definition" id="privacy"><sup class="footnote-definition-label">3</sup>
<p>It might seem nice to be able to make fields on a <code>struct</code> private today, but that requires us to pull in an number of other features. In particular, you need some methods that you can make public which do have access to the private fields. That's why there were attached methods before, and something like that could work with <code>impl</code>s but it would be tricky since <code>impl</code>s are a lot more flexible.<a href="#fnref:privacy" class="footnote-backref">↩</a></p>
</div>
<div class="footnote-definition" id="drop"><sup class="footnote-definition-label">4</sup>
<p>Early Rust had <code>resource</code> types which were basically a wrapper around a type that included a destructor. In some ways it was nice because most things didn't have destructors, but it also meant when you needed one you had to put your code through some contortions to make it work well with an attached destructor. Also, while it's tempting to say <code>Drop</code> is just like any other trait, it's not really because it has special meaning to the compiler.<a href="#fnref:drop" class="footnote-backref">↩</a></p>
</div>
<div class="footnote-definition" id="class-enum"><sup class="footnote-definition-label">5</sup>
<p>I expect had we kept classes it'd be common to have classes that just wrap an enum, since otherwise we wouldn't have had a way to attach methods to enums. Eventually we probably would have invented some kind of <code>enum class</code> syntax.<a href="#fnref:class-enum" class="footnote-backref">↩</a></p>
</div>
<div class="footnote-definition" id="rustfmt"><sup class="footnote-definition-label">6</sup>
<p>This is a big part of why rustfmt is so good, because that was how we rewrote the whole compiler every time we had a major breaking syntax change, which was not uncommon.<a href="#fnref:rustfmt" class="footnote-backref">↩</a></p>
</div>
]]></content><author><name>Eric Holk</name></author><summary type="html"><![CDATA[<p>While doing some housekeeping on my blog over the weekend, I can across an
<a href="https://web.archive.org/web/20120812052624/http://pcwalton.github.com/blog/2012/06/03/maximally-minimal-classes-for-rust/">ancient post</a> by Patrick Walton.
While I didn't realize it at the time, this post embodies what has become one of my core principles in program language design.[^spiky-blob]
In re-reading Patrick's post, this quote stood out in particular:</p>
<blockquote>
<p>Language design tends to go in cycles: we grow the language to accommodate new
functionality, then shrink the language as we discover ways in which the
features can be orthogonally integrated into the rest of the system. Classes
seem to me to be on the upward trajectory of complexity; now it’s time to
shrink them down. At the same time, we shouldn’t sacrifice the functionality
that they enable.</p>
</blockquote>
<p>This cycle of growing and shrinking as a key part of the process in the early days of Rust.
Upon reading this section, I found myself asking "how could we shrink Rust today?"</p>
]]></summary></entry><entry><title type="html">Rethinking Rust&apos;s Function Declaration Syntax</title><link href="https://theincredibleholk.org/blog/2023/12/15/rethinking-rusts-function-declaration-syntax/" rel="alternate" type="text/html" title="Eric Holk"/><published>2023-12-15T00:00:00+00:00</published><updated>2023-12-15T00:00:00+00:00</updated><id>https://theincredibleholk.org/blog/2023/12/15/rethinking-rusts-function-declaration-syntax/</id><content type="html" xml:base="https://theincredibleholk.org/blog/2023/12/15/rethinking-rusts-function-declaration-syntax/"><![CDATA[<p>We had a <a href="https://rust-lang.zulipchat.com/#narrow/stream/213817-t-lang/topic/Where.20to.20talk.20about.20.60try.20.7B.7D.60.2C.20.60yeet.60.2C.20etc.3F">fun discussion</a> in <a href="https://rust-lang.zulipchat.com/#narrow/stream/213817-t-lang">#t-lang</a> about possible new syntax for declaring functions in Rust.
There were a lot of cool ideas put forward, and while mulling them over I realized a lot of them work nicely together and can be introduced in a backwards-compatible way to give us some cool new capabilities.
While these were fresh in my mind and I'm feeling excited about them, I wanted to write them in one place.</p>
<!-- MORE -->
<p>For background, top level functions in Rust look sort of like this:</p>
<pre style="background-color:#ffffff;">
<span style="font-weight:bold;color:#a71d5d;">fn </span><span style="font-weight:bold;color:#795da3;">foo</span><span style="color:#323232;">(x: </span><span style="font-weight:bold;color:#a71d5d;">i32</span><span style="color:#323232;">) -&gt; </span><span style="font-weight:bold;color:#a71d5d;">i32 </span><span style="color:#323232;">{
</span><span style="color:#323232;">    x </span><span style="font-weight:bold;color:#a71d5d;">+ </span><span style="color:#0086b3;">1
</span><span style="color:#323232;">}
</span></pre>
<p>In Rust 2018, we added <code>async fn</code>:</p>
<pre style="background-color:#ffffff;">
<span style="color:#323232;">async </span><span style="font-weight:bold;color:#a71d5d;">fn </span><span style="font-weight:bold;color:#795da3;">foo</span><span style="color:#323232;">(x: </span><span style="font-weight:bold;color:#a71d5d;">i32</span><span style="color:#323232;">) -&gt; </span><span style="font-weight:bold;color:#a71d5d;">i32 </span><span style="color:#323232;">{
</span><span style="color:#323232;">    x </span><span style="font-weight:bold;color:#a71d5d;">+ </span><span style="color:#0086b3;">1
</span><span style="color:#323232;">}
</span></pre>
<p>While that one doesn't do anything particularly interesting, an async function gives you the ability to use <code>await</code> inside it.
It also secretly changes the return type from an <code>i32</code> to an <code>impl Future&lt;Output = i32&gt;</code>.
This is regarded by many to have been a mistake, and it's starting to cause issues now that we have async functions in traits since there is no way to add additional bounds like <code>Send</code> to the return type.
Anyway, <code>async fn foo</code> is mostly just syntactic sugar that desugars into:</p>
<pre style="background-color:#ffffff;">
<span style="font-weight:bold;color:#a71d5d;">fn </span><span style="font-weight:bold;color:#795da3;">foo</span><span style="color:#323232;">(x: </span><span style="font-weight:bold;color:#a71d5d;">i32</span><span style="color:#323232;">) -&gt; impl Future&lt;Output = </span><span style="font-weight:bold;color:#a71d5d;">i32</span><span style="color:#323232;">&gt; {
</span><span style="color:#323232;">    async { x </span><span style="font-weight:bold;color:#a71d5d;">+ </span><span style="color:#0086b3;">1 </span><span style="color:#323232;">}
</span><span style="color:#323232;">}
</span></pre>
<p>It's likely that Rust will gain a whole bunch of new keywords we can stick in front of <code>fn</code> in the future.<sup class="footnote-reference"><a href="#unsafe-const" id="fnref:unsafe-const">1</a></sup>
For example, nightly Rust just got support for <a href="https://github.com/rust-lang/rust/pull/118457"><code>gen fn</code></a> and <a href="https://github.com/rust-lang/rust/pull/118420"><code>async gen fn</code></a>.
Those desugar similar, by wrapping the return type in <code>impl Iterator</code> or <code>impl AsyncIterator</code> and wrapping the body in <code>gen { }</code> or <code>async gen { }</code>.</p>
<p>Another piece of sugar we could add is <code>try fn</code>, which is actually what started off the discussion thread today.
Following the pattern we've had so far, we'd expect to be able to write something like:</p>
<pre style="background-color:#ffffff;">
<span style="color:#323232;">try </span><span style="font-weight:bold;color:#a71d5d;">fn </span><span style="font-weight:bold;color:#795da3;">foo</span><span style="color:#323232;">() -&gt; </span><span style="font-weight:bold;color:#a71d5d;">i32 </span><span style="color:#323232;">{
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">let</span><span style="color:#323232;"> x </span><span style="font-weight:bold;color:#a71d5d;">= </span><span style="color:#62a35c;">read_number</span><span style="color:#323232;">()</span><span style="font-weight:bold;color:#a71d5d;">?</span><span style="color:#323232;">;
</span><span style="color:#323232;">    x
</span><span style="color:#323232;">}
</span></pre>
<p>and have this desugar to:</p>
<pre style="background-color:#ffffff;">
<span style="font-weight:bold;color:#a71d5d;">fn </span><span style="font-weight:bold;color:#795da3;">foo</span><span style="color:#323232;">() -&gt; impl Try&lt;Output = </span><span style="font-weight:bold;color:#a71d5d;">i32</span><span style="color:#323232;">, Residual = </span><span style="background-color:#f5f5f5;font-weight:bold;color:#b52a1d;">?</span><span style="font-weight:bold;color:#a71d5d;">??&gt; </span><span style="color:#323232;">{
</span><span style="color:#323232;">    try {
</span><span style="color:#323232;">        </span><span style="font-weight:bold;color:#a71d5d;">let</span><span style="color:#323232;"> x </span><span style="font-weight:bold;color:#a71d5d;">= </span><span style="color:#62a35c;">read_number</span><span style="color:#323232;">()</span><span style="font-weight:bold;color:#a71d5d;">?</span><span style="color:#323232;">;
</span><span style="color:#323232;">        x
</span><span style="color:#323232;">    }
</span><span style="color:#323232;">}
</span></pre>
<p>The problem is we need a hint for the <code>Residual</code> type.
The obvious thing to do would be to add something to the function header, like <code>try fn foo() -&gt; i32 throws E</code>.
But if you've ever looked at the <code>Residual</code> types for the <code>Try</code> impls in the standard library, you know that these can look pretty hairy and not particularly intuitive.
For example, to make a function that returns an <code>Option</code>, we'd need to write:</p>
<pre style="background-color:#ffffff;">
<span style="color:#323232;">try </span><span style="font-weight:bold;color:#a71d5d;">fn </span><span style="font-weight:bold;color:#795da3;">foo</span><span style="color:#323232;">() -&gt; </span><span style="font-weight:bold;color:#a71d5d;">i32</span><span style="color:#323232;"> throws Option&lt;Infallible&gt; {
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">let</span><span style="color:#323232;"> x </span><span style="font-weight:bold;color:#a71d5d;">= </span><span style="color:#62a35c;">read_number</span><span style="color:#323232;">()</span><span style="font-weight:bold;color:#a71d5d;">?</span><span style="color:#323232;">;
</span><span style="color:#323232;">    x
</span><span style="color:#323232;">}
</span></pre>
<p>This would give the compiler enough information to find the <a href="https://doc.rust-lang.org/std/ops/trait.Try.html#impl-Try-for-Option%3CT%3E"><code>Try</code> impl for <code>Option</code></a>.
But notice that we also could have just written <code>fn foo() -&gt; Option&lt;i32&gt;</code>, which is shorter and you don't have to figure out why my fallible function has an <code>Infallible</code> in it.</p>
<p>At this point, <a href="https://veykril.github.io/about/">Lukas Wirth</a> <a href="https://rust-lang.zulipchat.com/#narrow/stream/213817-t-lang/topic/Where.20to.20talk.20about.20.60try.20.7B.7D.60.2C.20.60yeet.60.2C.20etc.3F/near/408247587">observed</a> that they would rather see a shorthand for functions whose body is a single expression.
If we did this, we could write <code>try fn</code> as:</p>
<pre style="background-color:#ffffff;">
<span style="font-weight:bold;color:#a71d5d;">fn </span><span style="font-weight:bold;color:#795da3;">foo</span><span style="color:#323232;">() -&gt; Option&lt;</span><span style="font-weight:bold;color:#a71d5d;">i32</span><span style="color:#323232;">&gt; </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> try {
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">let</span><span style="color:#323232;"> x </span><span style="font-weight:bold;color:#a71d5d;">= </span><span style="color:#62a35c;">read_number</span><span style="color:#323232;">()</span><span style="font-weight:bold;color:#a71d5d;">?</span><span style="color:#323232;">;
</span><span style="color:#323232;">    x
</span><span style="color:#323232;">}
</span></pre>
<p>So that's pretty neat.</p>
<p>This also invites us to reconsider <code>async fn</code>.
We could instead write:</p>
<pre style="background-color:#ffffff;">
<span style="font-weight:bold;color:#a71d5d;">fn </span><span style="font-weight:bold;color:#795da3;">foo</span><span style="color:#323232;">() -&gt; impl Future&lt;Output = </span><span style="font-weight:bold;color:#a71d5d;">i32</span><span style="color:#323232;">&gt; </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> async {
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">let</span><span style="color:#323232;"> x </span><span style="font-weight:bold;color:#a71d5d;">= </span><span style="color:#62a35c;">read_number</span><span style="color:#323232;">().await;
</span><span style="color:#323232;">    x
</span><span style="color:#323232;">}
</span></pre>
<p>That's not too bad, but <code>impl Future&lt;Output = i32&gt;</code> is a bit wordy.
We could come up with some rules that would let you write <code>impl Future&lt;i32&gt;</code> instead, which honestly is how we usually read that out loud anyway.
But then <a href="https://rust-lang.zulipchat.com/#narrow/stream/213817-t-lang/topic/Where.20to.20talk.20about.20.60try.20.7B.7D.60.2C.20.60yeet.60.2C.20etc.3F/near/408258289">joboet</a> and <a href="https://rust-lang.zulipchat.com/#narrow/stream/213817-t-lang/topic/Where.20to.20talk.20about.20.60try.20.7B.7D.60.2C.20.60yeet.60.2C.20etc.3F/near/408263996">pitaj</a> pointed out that we could treat <code>Trait -&gt; Type</code> as shorthand for <code>Trait&lt;Output = Type&gt;</code>.
<a href="https://rust-lang.zulipchat.com/#narrow/stream/213817-t-lang/topic/Where.20to.20talk.20about.20.60try.20.7B.7D.60.2C.20.60yeet.60.2C.20etc.3F/near/408260627">TC</a> pointed out that we could probably generalize this to support <code>yields T</code> and <code>Iterator&lt;Item = T&gt;</code>.</p>
<p>So if we combined a few of these ideas, we'd be able to write:</p>
<pre style="background-color:#ffffff;">
<span style="font-weight:bold;color:#a71d5d;">fn </span><span style="font-weight:bold;color:#795da3;">foo</span><span style="color:#323232;">() -&gt; impl Future -&gt; </span><span style="font-weight:bold;color:#a71d5d;">i32 =</span><span style="color:#323232;"> async {
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">let</span><span style="color:#323232;"> x </span><span style="font-weight:bold;color:#a71d5d;">= </span><span style="color:#62a35c;">read_number</span><span style="color:#323232;">().await;
</span><span style="color:#323232;">    x
</span><span style="color:#323232;">}
</span></pre>
<p>I think this shows a lot of potential.
I want to try to generalize this a bit more though.
Instead of special-casing the <code>Output</code> associated type, we could create a set of attributes indicate an associated type can be used with trait keyword shorthands.
For example, define the <code>Future</code> and <code>Iterator</code> traits like this:</p>
<pre style="background-color:#ffffff;">
<span style="font-weight:bold;color:#a71d5d;">trait </span><span style="color:#323232;">Future {
</span><span style="color:#323232;">    #[keyword(return)]
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">type </span><span style="color:#323232;">Output;
</span><span style="color:#323232;">
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">fn </span><span style="font-weight:bold;color:#795da3;">poll</span><span style="color:#323232;">(self: Pin&lt;</span><span style="font-weight:bold;color:#a71d5d;">&amp;mut Self</span><span style="color:#323232;">&gt;, cx: </span><span style="font-weight:bold;color:#a71d5d;">&amp;mut </span><span style="color:#323232;">Context&lt;&#39;</span><span style="font-weight:bold;color:#a71d5d;">_</span><span style="color:#323232;">&gt;) -&gt; Poll&lt;</span><span style="font-weight:bold;color:#a71d5d;">Self::</span><span style="color:#323232;">Output&gt;;
</span><span style="color:#323232;">}
</span><span style="color:#323232;">
</span><span style="font-weight:bold;color:#a71d5d;">trait </span><span style="color:#323232;">Iterator {
</span><span style="color:#323232;">    #[keyword(yields)]
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">type </span><span style="color:#323232;">Item;
</span><span style="color:#323232;">
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">fn </span><span style="font-weight:bold;color:#795da3;">next</span><span style="color:#323232;">(</span><span style="font-weight:bold;color:#a71d5d;">&amp;mut </span><span style="color:#323232;">self) -&gt; Option&lt;</span><span style="font-weight:bold;color:#a71d5d;">Self::</span><span style="color:#323232;">Item&gt;;
</span><span style="color:#323232;">}
</span></pre>
<p>This would let us refer to <code>Future&lt;Output = T&gt;</code> as <code>Future -&gt; T</code> and <code>Iterator&lt;Item = T&gt;</code> as <code>Iterator yields T</code>.</p>
<p>We could even combine them:</p>
<pre style="background-color:#ffffff;">
<span style="font-weight:bold;color:#a71d5d;">trait </span><span style="color:#323232;">Coroutine&lt;R&gt; {
</span><span style="color:#323232;">    #[keyword(yields)]
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">type </span><span style="color:#323232;">Yield;
</span><span style="color:#323232;">
</span><span style="color:#323232;">    #[keyword(return)]
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">type </span><span style="color:#323232;">Return;
</span><span style="color:#323232;">
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">fn </span><span style="font-weight:bold;color:#795da3;">resume</span><span style="color:#323232;">(self: Pin&lt;</span><span style="font-weight:bold;color:#a71d5d;">&amp;mut Self</span><span style="color:#323232;">&gt;, arg: R) -&gt; CoroutineState&lt;</span><span style="font-weight:bold;color:#a71d5d;">Self::</span><span style="color:#323232;">Yield, </span><span style="font-weight:bold;color:#a71d5d;">Self::</span><span style="color:#323232;">Return&gt;;
</span><span style="color:#323232;">}
</span><span style="color:#323232;">
</span><span style="font-weight:bold;color:#a71d5d;">fn </span><span style="font-weight:bold;color:#795da3;">coroutine</span><span style="color:#323232;">() -&gt; impl Coroutine&lt;()&gt; -&gt; </span><span style="font-weight:bold;color:#a71d5d;">bool</span><span style="color:#323232;"> yields </span><span style="font-weight:bold;color:#a71d5d;">i32 = </span><span style="color:#323232;">|| {
</span><span style="color:#323232;">    </span><span style="background-color:#f5f5f5;font-weight:bold;color:#b52a1d;">yield</span><span style="color:#323232;"> </span><span style="color:#0086b3;">42</span><span style="color:#323232;">;
</span><span style="color:#323232;">    </span><span style="color:#0086b3;">true
</span><span style="color:#323232;">}
</span></pre>
<p>This would also let <a href="https://rust-lang.zulipchat.com/#narrow/stream/213817-t-lang/topic/Where.20to.20talk.20about.20.60try.20.7B.7D.60.2C.20.60yeet.60.2C.20etc.3F/near/408264357">remove some of the special handling</a> around the <code>Fn*</code> traits and we could expose this functionality to users so libraries could use this sugar in their own traits.</p>
<p>At this point, I'd like to take a step back and think about plain <code>fn</code> functions.
Notice that the following two would be equivalent:</p>
<pre style="background-color:#ffffff;">
<span style="font-weight:bold;color:#a71d5d;">fn </span><span style="font-weight:bold;color:#795da3;">foo</span><span style="color:#323232;">() -&gt; </span><span style="font-weight:bold;color:#a71d5d;">i32 </span><span style="color:#323232;">{
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">let</span><span style="color:#323232;"> number </span><span style="font-weight:bold;color:#a71d5d;">= </span><span style="color:#62a35c;">read_number</span><span style="color:#323232;">();
</span><span style="color:#323232;">    number
</span><span style="color:#323232;">}
</span><span style="color:#323232;">
</span><span style="font-weight:bold;color:#a71d5d;">fn </span><span style="font-weight:bold;color:#795da3;">foo</span><span style="color:#323232;">() -&gt; </span><span style="font-weight:bold;color:#a71d5d;">i32 = </span><span style="color:#323232;">{
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">let</span><span style="color:#323232;"> number </span><span style="font-weight:bold;color:#a71d5d;">= </span><span style="color:#62a35c;">read_number</span><span style="color:#323232;">();
</span><span style="color:#323232;">    number
</span><span style="color:#323232;">}
</span></pre>
<p>One way of think of this is that we've made the <code>=</code> optional.
But I'd like to think of it a different way.
Let's say instead we think of the <code>=</code> form as the standard function declaration syntax.
Then, if the function body consists of a single block, we can use a compressed syntax.
For a regular <code>{ }</code> block, that just looks like the function declaration syntax we're used to.
But for blocks with a keyword in front, like <code>async { }</code> or <code>try { }</code>, we say the keyword moves all the way to the front of the function header.
In addition, each block as an characteristic trait associated with it, so when we used the block shorthand for function declarations, we also wrap an <code>impl Trait</code> around the return type.</p>
<p>Here are some examples:</p>
<pre style="background-color:#ffffff;">
<span style="font-style:italic;color:#969896;">// async ////////////////////////////////////////
</span><span style="color:#323232;">
</span><span style="color:#323232;">async </span><span style="font-weight:bold;color:#a71d5d;">fn </span><span style="font-weight:bold;color:#795da3;">foo</span><span style="color:#323232;">() -&gt; </span><span style="font-weight:bold;color:#a71d5d;">i32 </span><span style="color:#323232;">{
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">let</span><span style="color:#323232;"> number </span><span style="font-weight:bold;color:#a71d5d;">= </span><span style="color:#62a35c;">read_number</span><span style="color:#323232;">().await;
</span><span style="color:#323232;">    number
</span><span style="color:#323232;">}
</span><span style="color:#323232;">
</span><span style="font-style:italic;color:#969896;">// desugars to:
</span><span style="color:#323232;">
</span><span style="font-weight:bold;color:#a71d5d;">fn </span><span style="font-weight:bold;color:#795da3;">foo</span><span style="color:#323232;">() </span><span style="font-weight:bold;color:#a71d5d;">= impl </span><span style="color:#323232;">Future&lt;Output = </span><span style="font-weight:bold;color:#a71d5d;">i32</span><span style="color:#323232;">&gt; = async {
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">let</span><span style="color:#323232;"> number </span><span style="font-weight:bold;color:#a71d5d;">= </span><span style="color:#62a35c;">read_number</span><span style="color:#323232;">().await;
</span><span style="color:#323232;">    number
</span><span style="color:#323232;">}
</span><span style="color:#323232;">
</span><span style="color:#323232;">
</span><span style="font-style:italic;color:#969896;">// gen //////////////////////////////////////////
</span><span style="color:#323232;">
</span><span style="color:#323232;">gen </span><span style="font-weight:bold;color:#a71d5d;">fn </span><span style="font-weight:bold;color:#795da3;">foo</span><span style="color:#323232;">() -&gt; </span><span style="font-weight:bold;color:#a71d5d;">i32 </span><span style="color:#323232;">{
</span><span style="color:#323232;">    </span><span style="background-color:#f5f5f5;font-weight:bold;color:#b52a1d;">yield</span><span style="color:#323232;"> </span><span style="color:#0086b3;">1</span><span style="color:#323232;">;
</span><span style="color:#323232;">    </span><span style="background-color:#f5f5f5;font-weight:bold;color:#b52a1d;">yield</span><span style="color:#323232;"> </span><span style="color:#0086b3;">2</span><span style="color:#323232;">;
</span><span style="color:#323232;">}
</span><span style="color:#323232;">
</span><span style="font-style:italic;color:#969896;">// desugars to:
</span><span style="color:#323232;">
</span><span style="font-weight:bold;color:#a71d5d;">fn </span><span style="font-weight:bold;color:#795da3;">foo</span><span style="color:#323232;">() </span><span style="font-weight:bold;color:#a71d5d;">= impl </span><span style="color:#323232;">Iterator&lt;Item = </span><span style="font-weight:bold;color:#a71d5d;">i32</span><span style="color:#323232;">&gt; = gen {
</span><span style="color:#323232;">    </span><span style="background-color:#f5f5f5;font-weight:bold;color:#b52a1d;">yield</span><span style="color:#323232;"> </span><span style="color:#0086b3;">1</span><span style="color:#323232;">;
</span><span style="color:#323232;">    </span><span style="background-color:#f5f5f5;font-weight:bold;color:#b52a1d;">yield</span><span style="color:#323232;"> </span><span style="color:#0086b3;">2</span><span style="color:#323232;">;
</span><span style="color:#323232;">}
</span><span style="color:#323232;">
</span><span style="font-style:italic;color:#969896;">// assuming `Iterator` is defined like:
</span><span style="color:#323232;">
</span><span style="font-weight:bold;color:#a71d5d;">trait </span><span style="color:#323232;">Iterator {
</span><span style="color:#323232;">    #[keyword(return)]
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">type </span><span style="color:#323232;">Item;
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">fn </span><span style="font-weight:bold;color:#795da3;">next</span><span style="color:#323232;">(</span><span style="font-weight:bold;color:#a71d5d;">&amp;mut </span><span style="color:#323232;">self) -&gt; Option&lt;</span><span style="font-weight:bold;color:#a71d5d;">Self::</span><span style="color:#323232;">Item&gt;;
</span><span style="color:#323232;">}
</span><span style="color:#323232;">
</span><span style="color:#323232;">
</span><span style="font-style:italic;color:#969896;">// async gen ////////////////////////////////////
</span><span style="color:#323232;">
</span><span style="color:#323232;">async gen </span><span style="font-weight:bold;color:#a71d5d;">fn </span><span style="font-weight:bold;color:#795da3;">foo</span><span style="color:#323232;">() -&gt; </span><span style="font-weight:bold;color:#a71d5d;">i32 </span><span style="color:#323232;">{
</span><span style="color:#323232;">    </span><span style="background-color:#f5f5f5;font-weight:bold;color:#b52a1d;">yield</span><span style="color:#323232;"> </span><span style="color:#0086b3;">1</span><span style="color:#323232;">;
</span><span style="color:#323232;">    </span><span style="background-color:#f5f5f5;font-weight:bold;color:#b52a1d;">yield</span><span style="color:#323232;"> </span><span style="color:#0086b3;">2</span><span style="color:#323232;">;
</span><span style="color:#323232;">}
</span><span style="color:#323232;">
</span><span style="font-style:italic;color:#969896;">// desugars to:
</span><span style="color:#323232;">
</span><span style="font-weight:bold;color:#a71d5d;">fn </span><span style="font-weight:bold;color:#795da3;">foo</span><span style="color:#323232;">() </span><span style="font-weight:bold;color:#a71d5d;">= impl </span><span style="color:#323232;">AsyncIterator&lt;Item = </span><span style="font-weight:bold;color:#a71d5d;">i32</span><span style="color:#323232;">&gt; = async gen {
</span><span style="color:#323232;">    </span><span style="background-color:#f5f5f5;font-weight:bold;color:#b52a1d;">yield</span><span style="color:#323232;"> </span><span style="color:#0086b3;">1</span><span style="color:#323232;">;
</span><span style="color:#323232;">    </span><span style="background-color:#f5f5f5;font-weight:bold;color:#b52a1d;">yield</span><span style="color:#323232;"> </span><span style="color:#0086b3;">2</span><span style="color:#323232;">;
</span><span style="color:#323232;">}
</span></pre>
<p>I've left try out because it's complicated.
You <em>could technically</em> do something like:</p>
<pre style="background-color:#ffffff;">
<span style="color:#323232;">try </span><span style="font-weight:bold;color:#a71d5d;">fn </span><span style="font-weight:bold;color:#795da3;">foo</span><span style="color:#323232;">() -&gt; </span><span style="font-weight:bold;color:#a71d5d;">i32</span><span style="color:#323232;"> throws Option&lt;Infallible&gt; {
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">let</span><span style="color:#323232;"> number </span><span style="font-weight:bold;color:#a71d5d;">= </span><span style="color:#62a35c;">read_number</span><span style="color:#323232;">()</span><span style="font-weight:bold;color:#a71d5d;">?</span><span style="color:#323232;">;
</span><span style="color:#323232;">    number
</span><span style="color:#323232;">}
</span></pre>
<p>but for <code>try</code> users usually want to know the concrete type.
So instead I'd expect most people to prefer the desugared form:</p>
<pre style="background-color:#ffffff;">
<span style="font-weight:bold;color:#a71d5d;">fn </span><span style="font-weight:bold;color:#795da3;">foo</span><span style="color:#323232;">() -&gt; Option&lt;</span><span style="font-weight:bold;color:#a71d5d;">i32</span><span style="color:#323232;">&gt; </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> try {
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">let</span><span style="color:#323232;"> number </span><span style="font-weight:bold;color:#a71d5d;">= </span><span style="color:#62a35c;">read_number</span><span style="color:#323232;">()</span><span style="font-weight:bold;color:#a71d5d;">?</span><span style="color:#323232;">;
</span><span style="color:#323232;">    number
</span><span style="color:#323232;">}
</span></pre>
<p>Note that the "pulling the keyword forward" transformation doesn't work because this function returns a concrete type and what I've proposed here is that pulling the keyword forward always adds an <code>impl Trait</code> rather than a concrete type.</p>
<p>Anyway, I'm pretty excited about this idea.<sup class="footnote-reference"><a href="#excited-for-now" id="fnref:excited-for-now">2</a></sup>
It feels like a consistent way to handle these connections between blocks, traits, and functions.
It's backwards compatible with the syntax we have so far, but it gives us a lot more expressiveness in cases where we're currently missing it.</p>
<hr />
<div class="footnote-definition" id="unsafe-const"><sup class="footnote-definition-label">1</sup>
<p>You can already do <code>unsafe fn</code> and <code>const fn</code> today, but these don't desugar in the same way as other proposed keywords here do.<a href="#fnref:unsafe-const" class="footnote-backref">↩</a></p>
</div>
<div class="footnote-definition" id="excited-for-now"><sup class="footnote-definition-label">2</sup>
<p>Of course, I also just started thinking about this today and cranked out a blog post, so I may hate it by Monday.<a href="#fnref:excited-for-now" class="footnote-backref">↩</a></p>
</div>
]]></content><author><name>Eric Holk</name></author><summary type="html"><![CDATA[<p>We had a <a href="https://rust-lang.zulipchat.com/#narrow/stream/213817-t-lang/topic/Where.20to.20talk.20about.20.60try.20.7B.7D.60.2C.20.60yeet.60.2C.20etc.3F">fun discussion</a> in <a href="https://rust-lang.zulipchat.com/#narrow/stream/213817-t-lang">#t-lang</a> about possible new syntax for declaring functions in Rust.
There were a lot of cool ideas put forward, and while mulling them over I realized a lot of them work nicely together and can be introduced in a backwards-compatible way to give us some cool new capabilities.
While these were fresh in my mind and I'm feeling excited about them, I wanted to write them in one place.</p>
]]></summary></entry><entry><title type="html">A Mechanism for Async Cancellation</title><link href="https://theincredibleholk.org/blog/2023/11/14/a-mechanism-for-async-cancellation/" rel="alternate" type="text/html" title="Eric Holk"/><published>2023-11-14T00:00:00+00:00</published><updated>2023-11-14T00:00:00+00:00</updated><id>https://theincredibleholk.org/blog/2023/11/14/a-mechanism-for-async-cancellation/</id><content type="html" xml:base="https://theincredibleholk.org/blog/2023/11/14/a-mechanism-for-async-cancellation/"><![CDATA[<p>One of the items on our <a href="https://github.com/orgs/rust-lang/projects/28/views/1">Async 2027 Roadmap</a> is to come up with some kind of <a href="https://github.com/rust-lang/wg-async/issues/297">asynchronous cleanup mechanism</a>, like <code>async Drop</code>.
There are some tricky design questions to making this work well, and we need to start thinking about these now if we want to have something ready by 2027.</p>
<p>In this post, I'd like to explore a low level mechanism for how we might implement async cancellation.
The goal is to explore both how an async executor<sup class="footnote-reference"><a href="#executor" id="fnref:executor">1</a></sup> would interact with cancellation, as well as to make sure that this mechanism would support reasonable surface-level semantics.
You can think of this as a kind of compilation target for higher level features, similar to how the Rust compiler lowers <code>async fn</code> into coroutines.</p>
<!-- MORE -->
<p>If you haven't read my last post on <a href="/blog/2023/11/08/cancellation-async-state-machines/">Cancellation and Async State Machines</a>, I'd encourage you to do so.
That post provides a kind of theoretical background for what we'll implement in this post.</p>
<h1 id="introducing-poll-cancel">Introducing <code>poll_cancel</code><a class="header-anchor" href="#introducing-poll-cancel">🔗</a></h1>
<p>Lately I've been working on a prototype implementation of <code>async</code>/<code>await</code>, as well as changes to <code>Future</code> and related traits, that supports more flexible cancellation.
I'd like to discuss this prototype, the tradeoffs made, and what I've learned about cancellation from the exercise.
Note that what I'm presenting here is <a href="https://en.wikipedia.org/wiki/Lambda_calculus#Alpha_equivalence">α-equivalent</a> to several previous proposals, including <a href="https://github.com/withoutboats/rfcs/blob/poll-drop-ready/text/0000-poll-drop-ready.md">Boats' <code>poll_drop_ready</code> RFC</a> and a <a href="https://internals.rust-lang.org/t/add-poll-cancel-to-future/16050">proposal by tvalloton on IRLO</a>.
My main contribution here is a prototype implementation that lets us write examples and explore their behavior.</p>
<h2 id="a-cancellable-future">A Cancellable <code>Future</code><a class="header-anchor" href="#a-cancellable-future">🔗</a></h2>
<p>The core of the idea is to extend the <code>Future</code> trait with a new <code>poll_cancel</code> that has a default implementation.
The new trait would look like this:</p>
<pre style="background-color:#ffffff;">
<span style="font-weight:bold;color:#a71d5d;">pub trait </span><span style="color:#323232;">Future {
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">type </span><span style="color:#323232;">Output;
</span><span style="color:#323232;">    
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">fn </span><span style="font-weight:bold;color:#795da3;">poll</span><span style="color:#323232;">(self: Pin&lt;</span><span style="font-weight:bold;color:#a71d5d;">&amp;mut Self</span><span style="color:#323232;">&gt;, cx: Context) -&gt; Poll&lt;</span><span style="font-weight:bold;color:#a71d5d;">Self::</span><span style="color:#323232;">Output&gt;;
</span><span style="color:#323232;">    
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">fn </span><span style="font-weight:bold;color:#795da3;">poll_cancel</span><span style="color:#323232;">(self: Pin&lt;</span><span style="font-weight:bold;color:#a71d5d;">&amp;mut Self</span><span style="color:#323232;">&gt;, cx: Context) -&gt; Poll&lt;()&gt; {
</span><span style="color:#323232;">        Poll::Ready(())
</span><span style="color:#323232;">    }
</span><span style="color:#323232;">}
</span></pre>
<p>In this new trait, <code>poll</code> has the same semantics as before.
The new <code>poll_cancel</code> method performs two operations.
First, it transitions the future's <a href="/blog/2023/11/08/cancellation-async-state-machines/">state machine</a> from its normal execution path to the correct cancellation state.
Second, <code>poll_cancel</code> continues to advance the state machine until the cancellation is complete.</p>
<p>The fact that <code>poll</code> and <code>poll_cancel</code> return different types highlights that fact that cancellation is a different exit from the future.
A cancelled future returns no value, so <code>poll_cancel</code> returns <code>Poll&lt;()&gt;</code> instead of <code>Poll&lt;Self::Output&gt;</code>
This matches what we saw in my <a href="/blog/2023/11/08/cancellation-async-state-machines/">previous post</a> where we had a different final state for a future that was cancelled versus one that completed normally.</p>
<p>There are some attractive properties about this approach.
The default implementation of <code>poll_cancel</code> leads to the same behavior that we have for cancellation today, where cancelling a future just means synchronously dropping it.
This suggests we can get a nice migration path, although adding a new default method to a trait is technically a breaking change.</p>
<p>There are significant shortcomings, which I'll discuss <a href="#weaknesses">further down</a>.
But first, I'd like to look at how <code>poll_cancel</code> works with <code>async</code> and <code>await</code>.</p>
<h2 id="cancellation-with-async-and-await">Cancellation with <code>async</code> and <code>await</code><a class="header-anchor" href="#cancellation-with-async-and-await">🔗</a></h2>
<p>Most people writing async Rust should not have to deal with <code>poll</code> directly.
Most of the time we use higher level constructs like <code>async</code> and <code>await</code> instead.
The nice thing about <code>async</code> and <code>await</code> in Rust is that there's nothing particularly magical about them.<sup class="footnote-reference"><a href="#magic" id="fnref:magic">2</a></sup>
The can be thought of as desugaring into lower level constructs, and this desugaring happens in a way that you could mostly implement them both as macros.<sup class="footnote-reference"><a href="#await-macro" id="fnref:await-macro">3</a></sup>
The primary benefit for building them into the language is that we can have nicer syntax and nicer diagnostics.</p>
<p>The fact that we can think of <code>async</code> and <code>await</code> as macros that desugars into lower level concepts means we can experiment with cancellation by writing a new set of macros that  that call <code>poll_cancel</code> in the appropriate place.
Most of the action will be in the changes we make to <code>await</code>.</p>
<p>The goal here is to come up with a desugaring that has predictable cancellation behavior that is also usually the desired behavior.</p>
<p>The somewhat surprising thing to me is that <code>await</code> mostly just forwards calls to <code>poll</code>, but doesn't have a lot of interesting future behavior.
The interesting behavior (such as making sure a <code>Waker</code> gets called sometime in the future) all happens in hand-written <code>Future</code> impls.
We can see this in the <a href="https://doc.rust-lang.org/stable/reference/expressions/await-expr.html#approximate-desugaring">approximate desugaring</a> of <code>await</code> from the Rust Language Docs:</p>
<pre style="background-color:#ffffff;">
<span style="font-weight:bold;color:#a71d5d;">match</span><span style="color:#323232;"> operand.</span><span style="color:#62a35c;">into_future</span><span style="color:#323232;">() {
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">mut</span><span style="color:#323232;"> pinned </span><span style="font-weight:bold;color:#a71d5d;">=&gt; loop </span><span style="color:#323232;">{
</span><span style="color:#323232;">        </span><span style="font-weight:bold;color:#a71d5d;">let mut</span><span style="color:#323232;"> pin </span><span style="font-weight:bold;color:#a71d5d;">= unsafe </span><span style="color:#323232;">{ Pin::new_unchecked(</span><span style="font-weight:bold;color:#a71d5d;">&amp;mut</span><span style="color:#323232;"> pinned) };
</span><span style="color:#323232;">        </span><span style="font-weight:bold;color:#a71d5d;">match </span><span style="color:#323232;">Pin::future::poll(Pin::borrow(</span><span style="font-weight:bold;color:#a71d5d;">&amp;mut</span><span style="color:#323232;"> pin), </span><span style="font-weight:bold;color:#a71d5d;">&amp;mut</span><span style="color:#323232;"> current_context) {
</span><span style="color:#323232;">            Poll::Ready(r) </span><span style="font-weight:bold;color:#a71d5d;">=&gt; break</span><span style="color:#323232;"> r,
</span><span style="color:#323232;">            Poll::Pending </span><span style="font-weight:bold;color:#a71d5d;">=&gt; </span><span style="background-color:#f5f5f5;font-weight:bold;color:#b52a1d;">yield</span><span style="color:#323232;"> Poll::Pending,
</span><span style="color:#323232;">        }
</span><span style="color:#323232;">    }
</span><span style="color:#323232;">}
</span></pre>
<p>This block of code runs when some code higher up the call stack calls our <code>poll</code> method.
What this block of code is doing is basically calling the awaited future's <code>poll</code> function in a loop.
If that future returns <code>Pending</code>, we yield <code>Pending</code>.
From this code, the compiler will generate a <code>Future::poll</code> function that returns <code>Pending</code> when the function would <code>yield Pending</code>.</p>
<p>This happens deeper than in the compiler than we can do with macros, but we can approximate something different.
Originally, the compiler actually generated an object that implemented <code>Generator</code> (now <a href="https://doc.rust-lang.org/nightly/std/ops/trait.Coroutine.html"><code>Coroutine</code></a>) and the standard library had a wrapper that adapted the <code>Generator</code> into a <code>Future</code>.
We'll use this approach for our prototype.</p>
<p>We'll want to handle cancellation similarly to how polling is handled, where <code>await</code> also forwards calls to <code>poll_cancel</code> along the await chain until we arrive at a future that knows how to do something interesting with cancellation.</p>
<p>Looking at how we might extend the desugaring of <code>await</code> to support <code>poll_cancel</code>, we need to distinguish whether we're on the cancel path or the normal execution path so we can call either <code>poll_cancel</code> or <code>poll</code> depending on the context.
We'll punt on this and assume we have a magic <code>is_cancelled</code> variable that can tell us this, which is similar to the <code>current_context</code> variable in the previous desugaring.</p>
<p>So let's see how this first step looks:</p>
<pre style="background-color:#ffffff;">
<span style="font-weight:bold;color:#a71d5d;">match</span><span style="color:#323232;"> operand.</span><span style="color:#62a35c;">into_future</span><span style="color:#323232;">() {
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">mut</span><span style="color:#323232;"> pinned </span><span style="font-weight:bold;color:#a71d5d;">=&gt; loop </span><span style="color:#323232;">{
</span><span style="color:#323232;">        </span><span style="font-weight:bold;color:#a71d5d;">let mut</span><span style="color:#323232;"> pin </span><span style="font-weight:bold;color:#a71d5d;">= unsafe </span><span style="color:#323232;">{ Pin::new_unchecked(</span><span style="font-weight:bold;color:#a71d5d;">&amp;mut</span><span style="color:#323232;"> pinned) };
</span><span style="color:#323232;">        </span><span style="font-weight:bold;color:#a71d5d;">if !</span><span style="color:#323232;">is_cancelled {
</span><span style="color:#323232;">            </span><span style="font-weight:bold;color:#a71d5d;">match </span><span style="color:#323232;">Pin::future::poll(Pin::borrow(</span><span style="font-weight:bold;color:#a71d5d;">&amp;mut</span><span style="color:#323232;"> pin), </span><span style="font-weight:bold;color:#a71d5d;">&amp;mut</span><span style="color:#323232;"> current_context) {
</span><span style="color:#323232;">                Poll::Ready(r) </span><span style="font-weight:bold;color:#a71d5d;">=&gt; break</span><span style="color:#323232;"> r,
</span><span style="color:#323232;">                Poll::Pending </span><span style="font-weight:bold;color:#a71d5d;">=&gt; </span><span style="background-color:#f5f5f5;font-weight:bold;color:#b52a1d;">yield</span><span style="color:#323232;"> Poll::Pending,
</span><span style="color:#323232;">            }
</span><span style="color:#323232;">        } </span><span style="font-weight:bold;color:#a71d5d;">else </span><span style="color:#323232;">{
</span><span style="color:#323232;">            </span><span style="font-weight:bold;color:#a71d5d;">match </span><span style="color:#323232;">Pin::future::poll_cancel(Pin::borrow(</span><span style="font-weight:bold;color:#a71d5d;">&amp;mut</span><span style="color:#323232;"> pin), </span><span style="font-weight:bold;color:#a71d5d;">&amp;mut</span><span style="color:#323232;"> current_context) {
</span><span style="color:#323232;">                Poll::Ready(()) </span><span style="font-weight:bold;color:#a71d5d;">=&gt; </span><span style="color:#323232;">panic!(</span><span style="color:#183691;">&quot;What do I do after cancelling?&quot;</span><span style="color:#323232;">),
</span><span style="color:#323232;">                Poll::Pending </span><span style="font-weight:bold;color:#a71d5d;">=&gt; </span><span style="background-color:#f5f5f5;font-weight:bold;color:#b52a1d;">yield</span><span style="color:#323232;"> Poll::Pending,
</span><span style="color:#323232;">            }
</span><span style="color:#323232;">        }
</span><span style="color:#323232;">    }
</span><span style="color:#323232;">}
</span></pre>
<p>It's like before, but we check if we are cancelled first.
If we are not, we continue with the previous behavior, calling <code>poll</code> and breaking out or the loop if the future is <code>Ready</code> or yielding <code>Pending</code> otherwise.</p>
<p>If we are cancelled we do almost the same thing, except we call <code>poll_cancel</code> instead.
If the cancellation is <code>Pending</code>, we yield again.
But if the cancellation is complete, we have to decide what to do next.
In the normal case, we have <code>break r</code>, which passes <code>r</code> out to the surrounding context, which is expecting a value of whatever type <code>r</code> is.
We can't do the same thing when the cancellation is complete because while <code>r</code> might be type <code>()</code>, we can't rely on that.
For now we panicked, since that type checks, but this obviously doesn't work.</p>
<p>We can get some inspiration from our state machines we saw <a href="/blog/2023/11/08/cancellation-async-state-machines/">earlier</a>.
Cancellation effectively means we have two exit states for the function: normal return and cancelled.
But functions in Rust only have one exit state<sup class="footnote-reference"><a href="#panic" id="fnref:panic">4</a></sup>, so we need to <em>reify</em> this into some data type that shows which final state you'd be in if you could have multiple final states.
It turns out the Rust standard library has one we can use for this purpose: <code>Result</code>.<sup class="footnote-reference"><a href="#option" id="fnref:option">5</a></sup>
So to report that an <code>async fn</code> or <code>async</code> block was successfully cancelled, we can return something like <code>Err(Cancelled)</code> and <code>Ok(T)</code> in the success case.
Factoring this into our approximate <code>await</code> desugaring gives us:</p>
<pre style="background-color:#ffffff;">
<span style="font-weight:bold;color:#a71d5d;">match</span><span style="color:#323232;"> operand.</span><span style="color:#62a35c;">into_future</span><span style="color:#323232;">() {
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">mut</span><span style="color:#323232;"> pinned </span><span style="font-weight:bold;color:#a71d5d;">=&gt; loop </span><span style="color:#323232;">{
</span><span style="color:#323232;">        </span><span style="font-weight:bold;color:#a71d5d;">let mut</span><span style="color:#323232;"> pin </span><span style="font-weight:bold;color:#a71d5d;">= unsafe </span><span style="color:#323232;">{ Pin::new_unchecked(</span><span style="font-weight:bold;color:#a71d5d;">&amp;mut</span><span style="color:#323232;"> pinned) };
</span><span style="color:#323232;">        </span><span style="font-weight:bold;color:#a71d5d;">if !</span><span style="color:#323232;">is_cancelled {
</span><span style="color:#323232;">            </span><span style="font-weight:bold;color:#a71d5d;">match </span><span style="color:#323232;">Pin::future::poll(Pin::borrow(</span><span style="font-weight:bold;color:#a71d5d;">&amp;mut</span><span style="color:#323232;"> pin), </span><span style="font-weight:bold;color:#a71d5d;">&amp;mut</span><span style="color:#323232;"> current_context) {
</span><span style="color:#323232;">                Poll::Ready(</span><span style="color:#0086b3;">Ok</span><span style="color:#323232;">(r)) </span><span style="font-weight:bold;color:#a71d5d;">=&gt; break</span><span style="color:#323232;"> r,
</span><span style="color:#323232;">                Poll::Pending </span><span style="font-weight:bold;color:#a71d5d;">=&gt; </span><span style="background-color:#f5f5f5;font-weight:bold;color:#b52a1d;">yield</span><span style="color:#323232;"> Poll::Pending,
</span><span style="color:#323232;">            }
</span><span style="color:#323232;">        } </span><span style="font-weight:bold;color:#a71d5d;">else </span><span style="color:#323232;">{
</span><span style="color:#323232;">            </span><span style="font-weight:bold;color:#a71d5d;">match </span><span style="color:#323232;">Pin::future::poll_cancel(Pin::borrow(</span><span style="font-weight:bold;color:#a71d5d;">&amp;mut</span><span style="color:#323232;"> pin), </span><span style="font-weight:bold;color:#a71d5d;">&amp;mut</span><span style="color:#323232;"> current_context) {
</span><span style="color:#323232;">                Poll::Ready(()) </span><span style="font-weight:bold;color:#a71d5d;">=&gt; return </span><span style="color:#0086b3;">Err</span><span style="color:#323232;">(Cancelled),
</span><span style="color:#323232;">                Poll::Pending </span><span style="font-weight:bold;color:#a71d5d;">=&gt; </span><span style="background-color:#f5f5f5;font-weight:bold;color:#b52a1d;">yield</span><span style="color:#323232;"> Poll::Pending,
</span><span style="color:#323232;">            }
</span><span style="color:#323232;">        }
</span><span style="color:#323232;">    }
</span><span style="color:#323232;">}
</span></pre>
<p>In the desugaring of <code>async {}</code>, we'll also need to wrap all the normal exit paths with <code>Ok()</code>.</p>
<h3 id="the-generator-adapter">The Generator Adapter<a class="header-anchor" href="#the-generator-adapter">🔗</a></h3>
<p>In the previous section I gave a rough sketch of how to desugar <code>async</code> and <code>await</code> into generators in a way that supports cancellation.
Now I want to fill in some of the details by looking at how this resulting generator becomes a future.</p>
<p>If we were implementing this for real in Rust, we'd probably just have the compiler implement <code>Future</code> directly, like it currently does for <code>async</code> blocks.
But, using generators lets us implement and experiment with this in a crate without having to modify the compiler.<sup class="footnote-reference"><a href="#async-coroutines" id="fnref:async-coroutines">6</a></sup></p>
<p>So, if we did everything right in the previous section, we should end up with a compiler-generated generator that implements <code>Generator&lt;(Context, bool), Yield = (), Return = Result&lt;T, Cancelled&gt;</code>, where <code>T</code> is the output type of the <code>Future</code> and <code>Cancelled</code> is just a marker tag like <code>struct Cancelled</code>.
The argument to the <code>resume</code> function, <code>(Context, bool)</code>, is a tuple containing the <code>Context</code> as well as a <code>bool</code> indicating whether the future is cancelled.
This <code>bool</code> would get bound to the <code>is_cancelled</code> variable in the <code>await</code> desugaring above.<sup class="footnote-reference"><a href="#cancelled-context" id="fnref:cancelled-context">7</a></sup></p>
<p>Now we can make these into futures as follows:<sup class="footnote-reference"><a href="#pseudo-code" id="fnref:pseudo-code">8</a></sup></p>
<pre style="background-color:#ffffff;">
<span style="font-weight:bold;color:#a71d5d;">impl</span><span style="color:#323232;">&lt;O, G&gt; Future </span><span style="font-weight:bold;color:#a71d5d;">for </span><span style="color:#323232;">G
</span><span style="font-weight:bold;color:#a71d5d;">where
</span><span style="color:#323232;">    G: core::ops::Generator&lt;PollState, Yield = (), Return = Result&lt;O, Cancelled&gt;&gt;,
</span><span style="color:#323232;">{
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">type </span><span style="color:#323232;">Output </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> O;
</span><span style="color:#323232;">
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">fn </span><span style="font-weight:bold;color:#795da3;">poll</span><span style="color:#323232;">(self: Pin&lt;</span><span style="font-weight:bold;color:#a71d5d;">&amp;mut Self</span><span style="color:#323232;">&gt;, cx: </span><span style="font-weight:bold;color:#a71d5d;">&amp;mut </span><span style="color:#323232;">Context&lt;&#39;</span><span style="font-weight:bold;color:#a71d5d;">_</span><span style="color:#323232;">&gt;) -&gt; Poll&lt;</span><span style="font-weight:bold;color:#a71d5d;">Self::</span><span style="color:#323232;">Output&gt; {
</span><span style="color:#323232;">        </span><span style="font-weight:bold;color:#a71d5d;">match </span><span style="color:#323232;">self.</span><span style="color:#62a35c;">resume</span><span style="color:#323232;">((cx, </span><span style="color:#0086b3;">false</span><span style="color:#323232;">)) {
</span><span style="color:#323232;">            GeneratorState::Yielded(()) </span><span style="font-weight:bold;color:#a71d5d;">=&gt; </span><span style="color:#323232;">Poll::Pending,
</span><span style="color:#323232;">            GeneratorState::Complete(</span><span style="color:#0086b3;">Ok</span><span style="color:#323232;">(v)) </span><span style="font-weight:bold;color:#a71d5d;">=&gt; </span><span style="color:#323232;">Poll::Ready(v),
</span><span style="color:#323232;">            GeneratorState::Complete(</span><span style="color:#0086b3;">Err</span><span style="color:#323232;">(Cancelled)) </span><span style="font-weight:bold;color:#a71d5d;">=&gt; </span><span style="color:#323232;">panic!(</span><span style="color:#183691;">&quot;child future cancelled itself&quot;</span><span style="color:#323232;">),
</span><span style="color:#323232;">        }
</span><span style="color:#323232;">    }
</span><span style="color:#323232;">
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">fn </span><span style="font-weight:bold;color:#795da3;">poll_cancel</span><span style="color:#323232;">(self: Pin&lt;</span><span style="font-weight:bold;color:#a71d5d;">&amp;mut Self</span><span style="color:#323232;">&gt;, cx: </span><span style="font-weight:bold;color:#a71d5d;">&amp;mut </span><span style="color:#323232;">Context&lt;&#39;</span><span style="font-weight:bold;color:#a71d5d;">_</span><span style="color:#323232;">&gt;) -&gt; Poll&lt;()&gt; {
</span><span style="color:#323232;">        </span><span style="font-weight:bold;color:#a71d5d;">match </span><span style="color:#323232;">self.</span><span style="color:#62a35c;">resume</span><span style="color:#323232;">((cx, </span><span style="color:#0086b3;">true</span><span style="color:#323232;">)) {
</span><span style="color:#323232;">            GeneratorState::Yielded(()) </span><span style="font-weight:bold;color:#a71d5d;">=&gt; </span><span style="color:#323232;">Poll::Pending,
</span><span style="color:#323232;">            GeneratorState::Complete(</span><span style="color:#0086b3;">Ok</span><span style="color:#323232;">(</span><span style="font-weight:bold;color:#a71d5d;">_</span><span style="color:#323232;">)) </span><span style="font-weight:bold;color:#a71d5d;">=&gt; </span><span style="color:#323232;">{
</span><span style="color:#323232;">                panic!(</span><span style="color:#183691;">&quot;future completed after being cancelled&quot;</span><span style="color:#323232;">)
</span><span style="color:#323232;">            }
</span><span style="color:#323232;">            GeneratorState::Complete(</span><span style="color:#0086b3;">Err</span><span style="color:#323232;">(Cancelled)) </span><span style="font-weight:bold;color:#a71d5d;">=&gt; </span><span style="color:#323232;">Poll::Ready(()),
</span><span style="color:#323232;">        }
</span><span style="color:#323232;">    }
</span><span style="color:#323232;">}
</span></pre>
<p>Our implementation needs to cover both <code>poll</code> and <code>poll_cancel</code>, but they are both pretty similar.
Each one forwards the call to the generator's <code>resume</code> method and then adapts the result into something expected by the surrounding async code.</p>
<p>Generators only have a <code>resume</code> method, but in this post we've extended <code>Future</code> to have two methods.
So when we go from a call to <code>poll</code> or <code>poll_cancel</code> to a call to <code>resume</code>, we need to tell <code>resume</code> which version it is.
We do this by passing an extra boolean, which the generator uses to determine whether it should go along the normal execution path or the cancellation path.</p>
<p>Generators return either <code>Yielded</code> or <code>Complete</code>, which for futures correspond to <code>Pending</code> and <code>Ready</code>.
Because we've made <code>resume</code> return a <code>Result</code> to indicate whether the future was cancelled, we have some more cases to check.
We don't want to bubble the <code>Result</code> out to user code; we want to keep it hidden <a href="/blog/2014/05/24/monads-as-a-design-pattern/">inside the monad</a>.
From the user's perspective, this is still just a future that evalutes to a <code>T</code>, not a fallible future.</p>
<p>So we have this invariant, that in <code>poll</code>, <code>resume</code> should never return an <code>Err(Cancelled)</code> and in <code>poll_cancel</code>, <code>resume</code> should never return <code>Ok</code>.
The first case would mean that the future cancelled itself, which is not the way cancellation works in Rust.
The second case would mean the cancellation failed, that after being cancelled the future completed normally.
In this design we're also choosing not to model that case.<sup class="footnote-reference"><a href="#complete-after-cancel" id="fnref:complete-after-cancel">9</a></sup>
In an ideal world, the compiler would be able to prove both of these cases are unreachable, or we'd design the API so that these cases aren't even possible to write.
Honestly, this is one of the aspects of this design that I'm least satisfied with.
I'd like to experiment with different factorings that would let us get rid of the panics.</p>
<p>Anyway, that's the rough idea of how this design works.
I haven't written the complete implementation here because I find prose more informative than code, but I do have a prototype implementation at <a href="https://github.com/eholk/explicit-async-cancellation">https://github.com/eholk/explicit-async-cancellation</a> if you want to see the full details.</p>
<p>But for now, let's see what this lets us do.</p>
<h1 id="scenarios">Scenarios<a class="header-anchor" href="#scenarios">🔗</a></h1>
<p>My prototype includes a macro called <a href="https://github.com/eholk/explicit-async-cancellation/blob/9991b5db249f1f004f0530462d215421abb500f5/src/cancelable.rs#L80"><code>async_cancel!</code></a>, which is similar to <code>async {}</code> blocks, except with support for cancellation handlers.
This is meant to be paired with the <a href="https://github.com/eholk/explicit-async-cancellation/blob/9991b5db249f1f004f0530462d215421abb500f5/src/cancelable.rs#L116"><code>awaitc!</code></a> macro, which is analogous to <code>.await</code>, but with support for cancellation handlers.<sup class="footnote-reference"><a href="#scheme" id="fnref:scheme">10</a></sup>
Because these are not built in syntax, they are ugly and hard to read in the examples I've prototyped so far.
So in this section, I'll write out examples as if <code>async</code> and <code>await</code> supported cancellation handlers in the way described above.</p>
<p>First, I want to introduce a convenience called <a href="https://github.com/eholk/explicit-async-cancellation/blob/9991b5db249f1f004f0530462d215421abb500f5/src/cancelable.rs#L186"><code>on_cancel</code></a>.
This gives us a way to run asynchronous code along the cancellation path.
This is important to show that everything actually works how we want, but I'm not really a fan of the API and would prefer it not be the standard way to run code on cancellation.
Think of this as a placeholder for something like <code>defer {}</code> blocks or <code>async Drop</code>.<sup class="footnote-reference"><a href="#defer-drop" id="fnref:defer-drop">11</a></sup>
I've implemented <code>on_cancel</code> as an extension method on futures that takes a future and runs that future on the parent future's cancellation path.
That's a little confusing to read, but in code it looks like this:</p>
<pre style="background-color:#ffffff;">
<span style="color:#323232;">async {
</span><span style="color:#323232;">    </span><span style="color:#62a35c;">do_something</span><span style="color:#323232;">().await;
</span><span style="color:#323232;">    println!(</span><span style="color:#183691;">&quot;all done!&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">}.</span><span style="color:#62a35c;">on_cancel</span><span style="color:#323232;">(async {
</span><span style="color:#323232;">    println! (</span><span style="color:#183691;">&quot;cancelled!&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">}).await;
</span></pre>
<p>In my examples, I'll also make liberal use of futures like <a href="https://github.com/eholk/explicit-async-cancellation/blob/9991b5db249f1f004f0530462d215421abb500f5/src/cancelable.rs#L162"><code>pending()</code></a> and <a href="https://github.com/eholk/explicit-async-cancellation/blob/9991b5db249f1f004f0530462d215421abb500f5/src/cancelable.rs#L148"><code>ready()</code></a>, which never complete and immediately complete respectively.</p>
<h2 id="a-cancellation-aware-executor">A Cancellation-aware Executor<a class="header-anchor" href="#a-cancellation-aware-executor">🔗</a></h2>
<p>The first thing we need is an executor that is aware of cancellation.
We'll make a simple one that runs a single task, similar to <code>block_on</code>.
If for some reason the executor is dropped before the root task completes, then in the executor's drop function will call <code>poll_cancel</code> on the root task until it's complete.
In pseudo-code, our executor looks something like this (actual code is <a href="https://github.com/eholk/explicit-async-cancellation/blob/main/src/executor.rs">here</a>):</p>
<pre style="background-color:#ffffff;">
<span style="font-weight:bold;color:#a71d5d;">impl</span><span style="color:#323232;">&lt;T&gt; Executor&lt;T&gt; {
</span><span style="color:#323232;">    </span><span style="font-style:italic;color:#969896;">/// Run the root task to completion
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">fn </span><span style="font-weight:bold;color:#795da3;">run</span><span style="color:#323232;">(</span><span style="font-weight:bold;color:#a71d5d;">&amp;mut </span><span style="color:#323232;">self) -&gt; T {
</span><span style="color:#323232;">        </span><span style="font-weight:bold;color:#a71d5d;">loop </span><span style="color:#323232;">{
</span><span style="color:#323232;">            </span><span style="font-weight:bold;color:#a71d5d;">match </span><span style="color:#323232;">self.</span><span style="color:#62a35c;">poll_once</span><span style="color:#323232;">() {
</span><span style="color:#323232;">                Poll::Pending </span><span style="font-weight:bold;color:#a71d5d;">=&gt; continue</span><span style="color:#323232;">,
</span><span style="color:#323232;">                Poll::Ready(result) </span><span style="font-weight:bold;color:#a71d5d;">=&gt; return</span><span style="color:#323232;"> result,
</span><span style="color:#323232;">            }
</span><span style="color:#323232;">        }
</span><span style="color:#323232;">    }
</span><span style="color:#323232;">    
</span><span style="color:#323232;">    </span><span style="font-style:italic;color:#969896;">/// Poll the root task once
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">fn </span><span style="font-weight:bold;color:#795da3;">poll_once</span><span style="color:#323232;">(</span><span style="font-weight:bold;color:#a71d5d;">&amp;mut </span><span style="color:#323232;">self) -&gt; </span><span style="font-weight:bold;color:#a71d5d;">Poll::</span><span style="color:#323232;">Pending {
</span><span style="color:#323232;">        </span><span style="font-weight:bold;color:#a71d5d;">let</span><span style="color:#323232;"> context </span><span style="font-weight:bold;color:#a71d5d;">= </span><span style="color:#323232;">self.</span><span style="color:#62a35c;">context</span><span style="color:#323232;">();
</span><span style="color:#323232;">        self.root_task.</span><span style="color:#62a35c;">poll</span><span style="color:#323232;">(context)
</span><span style="color:#323232;">    }
</span><span style="color:#323232;">    
</span><span style="color:#323232;">    </span><span style="font-style:italic;color:#969896;">// Definition of `context` is omitted
</span><span style="color:#323232;">}
</span><span style="color:#323232;">
</span><span style="font-weight:bold;color:#a71d5d;">impl</span><span style="color:#323232;">&lt;T&gt; Drop </span><span style="font-weight:bold;color:#a71d5d;">for </span><span style="color:#323232;">Executor&lt;T&gt; {
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">fn </span><span style="font-weight:bold;color:#795da3;">drop</span><span style="color:#323232;">(</span><span style="font-weight:bold;color:#a71d5d;">&amp;mut </span><span style="color:#323232;">self) {
</span><span style="color:#323232;">        </span><span style="font-weight:bold;color:#a71d5d;">let</span><span style="color:#323232;"> context </span><span style="font-weight:bold;color:#a71d5d;">= </span><span style="color:#323232;">self.</span><span style="color:#62a35c;">context</span><span style="color:#323232;">();
</span><span style="color:#323232;">        </span><span style="font-weight:bold;color:#a71d5d;">while let </span><span style="color:#323232;">Poll::Pending </span><span style="font-weight:bold;color:#a71d5d;">= </span><span style="color:#323232;">self.root_task.</span><span style="color:#62a35c;">poll_cancel</span><span style="color:#323232;">(context) {}
</span><span style="color:#323232;">    }
</span><span style="color:#323232;">}
</span></pre>
<p>This gives us just enough to experiment with cancellation behavior.
We can run simple futures like this:</p>
<pre style="background-color:#ffffff;">
<span style="font-weight:bold;color:#a71d5d;">fn </span><span style="font-weight:bold;color:#795da3;">main</span><span style="color:#323232;">() {
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">let</span><span style="color:#323232;"> root_task </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> async {
</span><span style="color:#323232;">        </span><span style="color:#0086b3;">42
</span><span style="color:#323232;">    };
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">let mut</span><span style="color:#323232;"> exec </span><span style="font-weight:bold;color:#a71d5d;">= </span><span style="color:#323232;">Executor::new(root_task);
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">let</span><span style="color:#323232;"> result </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> exec.</span><span style="color:#62a35c;">run</span><span style="color:#323232;">();
</span><span style="color:#323232;">    println!(</span><span style="color:#183691;">&quot;the root task returned </span><span style="color:#0086b3;">{result}</span><span style="color:#183691;">&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">}
</span></pre>
<p>This program would run and print out</p>
<pre><code>the root task returned 42
</code></pre>
<p>We have some more power though.
Rather than using <code>run</code> to poll to completion, we can use <code>poll_once</code> some number of times to leave the future in an incomplete state.
If the executor is dropped before the future is complete, it will run the cancellation path in the executor's <code>drop</code> function.</p>
<p>Here's a basic example showing cancellation:</p>
<pre style="background-color:#ffffff;">
<span style="font-weight:bold;color:#a71d5d;">fn </span><span style="font-weight:bold;color:#795da3;">main</span><span style="color:#323232;">() {
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">let</span><span style="color:#323232;"> root_task </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> async {
</span><span style="color:#323232;">        </span><span style="color:#62a35c;">pending</span><span style="color:#323232;">().await;
</span><span style="color:#323232;">        println!(</span><span style="color:#183691;">&quot;all done!&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">    }.</span><span style="color:#62a35c;">on_cancel</span><span style="color:#323232;">(async {
</span><span style="color:#323232;">        println!(</span><span style="color:#183691;">&quot;the task did not finish&quot;</span><span style="color:#323232;">)
</span><span style="color:#323232;">    });
</span><span style="color:#323232;">    
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">let mut</span><span style="color:#323232;"> exec </span><span style="font-weight:bold;color:#a71d5d;">= </span><span style="color:#323232;">Executor::new(root_task);
</span><span style="color:#323232;">    exec.</span><span style="color:#62a35c;">poll_once</span><span style="color:#323232;">(); </span><span style="font-style:italic;color:#969896;">// pending
</span><span style="color:#323232;">    exec.</span><span style="color:#62a35c;">poll_once</span><span style="color:#323232;">(); </span><span style="font-style:italic;color:#969896;">// still pending
</span><span style="color:#323232;">    </span><span style="color:#62a35c;">drop</span><span style="color:#323232;">(exec); </span><span style="font-style:italic;color:#969896;">// just give up
</span><span style="color:#323232;">}
</span></pre>
<p>In this example, the root task blocks on <code>pending()</code>, which will never finish.
But we attached a cancellation handler that runs when the executor is dropped before finishing the future.
Running this program produces:</p>
<pre><code>the task did not finish
</code></pre>
<p>So we have the basics of cancellation support and cancellation handlers.
Now lets see how this composes with more interesting futures.</p>
<h2 id="cancellation-aware-combinators">Cancellation-aware Combinators<a class="header-anchor" href="#cancellation-aware-combinators">🔗</a></h2>
<p>I'm using "combinators" here to mean futures which combine or otherwise transform other futures in interesting ways.<sup class="footnote-reference"><a href="#mini-executor" id="fnref:mini-executor">12</a></sup>
By this definition, we've already seen the <code>on_cancel</code> combinator, which lets you override the cancellation behavior of a future.</p>
<p>Let's consider another one: <code>race</code>.
We'll use a very simplified version of <code>race</code>, which looks like <code>a.race(b)</code>.
This takes a future <code>a</code> and a future <code>b</code> and runs them both concurrently.
When one finishes, <code>race</code> will cancel the other and return the value from the one that finished first.</p>
<p>The <a href="https://github.com/eholk/explicit-async-cancellation/blob/9991b5db249f1f004f0530462d215421abb500f5/src/cancelable.rs#L229">code for this</a> looks horrible, so I'll leave it out of the post and focus mainly on how it looks to use it.</p>
<p>Here's an example using <code>race</code> with a cancellation handler:</p>
<pre style="background-color:#ffffff;">
<span style="font-weight:bold;color:#a71d5d;">fn </span><span style="font-weight:bold;color:#795da3;">main</span><span style="color:#323232;">() {
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">let</span><span style="color:#323232;"> root_task </span><span style="font-weight:bold;color:#a71d5d;">= </span><span style="color:#62a35c;">pending</span><span style="color:#323232;">().</span><span style="color:#62a35c;">on_cancel</span><span style="color:#323232;">(async {
</span><span style="color:#323232;">        println!(</span><span style="color:#183691;">&quot;future `a` was cancelled&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">    }).</span><span style="color:#62a35c;">race</span><span style="color:#323232;">(async {
</span><span style="color:#323232;">        </span><span style="color:#0086b3;">42
</span><span style="color:#323232;">    });
</span><span style="color:#323232;">    
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">let mut</span><span style="color:#323232;"> exec </span><span style="font-weight:bold;color:#a71d5d;">= </span><span style="color:#323232;">Executor::new(root_task);
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">let</span><span style="color:#323232;"> result </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> exec.</span><span style="color:#62a35c;">run</span><span style="color:#323232;">();
</span><span style="color:#323232;">    
</span><span style="color:#323232;">    println!(</span><span style="color:#183691;">&quot;result: </span><span style="color:#0086b3;">{result}</span><span style="color:#183691;">&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">}
</span></pre>
<p>In this example, our root task consists of a race between <code>pending()</code> and <code>async { 42 }</code>.
The <code>pending()</code> future never finishes.
We've attached a cancellation handler to it so we can see some indication that it was cancelled.
So the race combinator sees that the second future returns <code>42</code> while the first is still pending.
Before returning, it runs the first future's cancellation handler, printing <code>future `a` was cancelled</code>.
Then it returns 42 as the overall value of the race future.
This program's output is:</p>
<pre><code>future `a` was cancelled
result: 42
</code></pre>
<h2 id="cancel-during-cancellation">Cancel during Cancellation<a class="header-anchor" href="#cancel-during-cancellation">🔗</a></h2>
<p>The <code>poll_cancel</code> mechanism we're discussing is able to support what I earlier called <a href="/blog/2023/11/08/cancellation-async-state-machines/#1-cancelling-a-cancellation-is-idempotent"><em>idempotent cancellation</em></a>.<sup class="footnote-reference"><a href="#recursive-cancellation" id="fnref:recursive-cancellation">13</a></sup>
This means that if you cancel a future whose cancellation process has already started then the cancellation process continues as before.</p>
<p>To get a feel for how this works, let's look at a rather contrived example:</p>
<table class="codenum"><tbody><tr><td>
<pre><code>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41</code></pre>
</td><td><pre style="background-color:#ffffff;">
<span style="font-weight:bold;color:#a71d5d;">fn </span><span style="font-weight:bold;color:#795da3;">main</span><span style="color:#323232;">() {
</span><span style="color:#323232;">    </span><span style="font-style:italic;color:#969896;">// we&#39;ll use `done` to create a future that blocks until some other code
</span><span style="color:#323232;">    </span><span style="font-style:italic;color:#969896;">// sets the `done` to true.
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">let</span><span style="color:#323232;"> done </span><span style="font-weight:bold;color:#a71d5d;">= &amp;</span><span style="color:#323232;">RefCell::new(</span><span style="color:#0086b3;">false</span><span style="color:#323232;">);
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">let</span><span style="color:#323232;"> root_task </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> async { 
</span><span style="color:#323232;">        </span><span style="font-style:italic;color:#969896;">// we&#39;re going to race `a` and `b`, so we&#39;ll create those two futures
</span><span style="color:#323232;">        </span><span style="font-style:italic;color:#969896;">// separately.
</span><span style="color:#323232;">        </span><span style="font-weight:bold;color:#a71d5d;">let</span><span style="color:#323232;"> a </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> async { </span><span style="color:#0086b3;">42 </span><span style="color:#323232;">};
</span><span style="color:#323232;">        </span><span style="font-style:italic;color:#969896;">// when b cancels, we want a cancellation handler that can print a
</span><span style="color:#323232;">        </span><span style="font-style:italic;color:#969896;">// message for us the first time it&#39;s polled. We&#39;ll use
</span><span style="color:#323232;">        </span><span style="font-style:italic;color:#969896;">// `cancel_started` to track that.
</span><span style="color:#323232;">        </span><span style="font-weight:bold;color:#a71d5d;">let mut</span><span style="color:#323232;"> cancel_started </span><span style="font-weight:bold;color:#a71d5d;">= </span><span style="color:#0086b3;">false</span><span style="color:#323232;">;
</span><span style="color:#323232;">        </span><span style="font-weight:bold;color:#a71d5d;">let</span><span style="color:#323232;"> b </span><span style="font-weight:bold;color:#a71d5d;">= </span><span style="color:#62a35c;">pending</span><span style="color:#323232;">().</span><span style="color:#62a35c;">on_cancel</span><span style="color:#323232;">(</span><span style="color:#62a35c;">poll_fn</span><span style="color:#323232;">(|_| {
</span><span style="color:#323232;">            </span><span style="font-weight:bold;color:#a71d5d;">if !</span><span style="color:#323232;">cancel_started {
</span><span style="color:#323232;">                </span><span style="font-style:italic;color:#969896;">// print a message if it&#39;s our first time through.
</span><span style="color:#323232;">                println!(</span><span style="color:#183691;">&quot;begin cancelling `b`&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">                cancel_started </span><span style="font-weight:bold;color:#a71d5d;">= </span><span style="color:#0086b3;">true</span><span style="color:#323232;">;
</span><span style="color:#323232;">            }
</span><span style="color:#323232;">            </span><span style="font-style:italic;color:#969896;">// Only complete if someone has set `done` to true.
</span><span style="color:#323232;">            </span><span style="font-weight:bold;color:#a71d5d;">if *</span><span style="color:#323232;">done.</span><span style="color:#62a35c;">borrow</span><span style="color:#323232;">() {
</span><span style="color:#323232;">                println!(</span><span style="color:#183691;">&quot;cancellation of `b` complete&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">                Poll::Ready(())
</span><span style="color:#323232;">            } </span><span style="font-weight:bold;color:#a71d5d;">else </span><span style="color:#323232;">{
</span><span style="color:#323232;">                Poll::Pending
</span><span style="color:#323232;">            }
</span><span style="color:#323232;">        }));
</span><span style="color:#323232;">        
</span><span style="color:#323232;">        a.</span><span style="color:#62a35c;">race</span><span style="color:#323232;">(b).</span><span style="color:#62a35c;">on_cancel</span><span style="color:#323232;">(async {
</span><span style="color:#323232;">            println!(</span><span style="color:#183691;">&quot;cancelling `race` future&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">        }).await;
</span><span style="color:#323232;">    }.</span><span style="color:#62a35c;">on_cancel</span><span style="color:#323232;">(async {
</span><span style="color:#323232;">        println!(</span><span style="color:#183691;">&quot;cancelling root future&quot;</span><span style="color:#323232;">);
</span><span style="color:#323232;">    });
</span><span style="color:#323232;">    
</span><span style="color:#323232;">    </span><span style="font-style:italic;color:#969896;">// Poll the futures a few time, then let the executor shut down
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">let mut</span><span style="color:#323232;"> executor </span><span style="font-weight:bold;color:#a71d5d;">= </span><span style="color:#323232;">Executor::new(root_task);
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">let _ =</span><span style="color:#323232;"> executor.</span><span style="color:#62a35c;">poll</span><span style="color:#323232;">();
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">let _ =</span><span style="color:#323232;"> executor.</span><span style="color:#62a35c;">poll</span><span style="color:#323232;">();
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">let _ =</span><span style="color:#323232;"> executor.</span><span style="color:#62a35c;">poll</span><span style="color:#323232;">();
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">*</span><span style="color:#323232;">done.</span><span style="color:#62a35c;">borrow_mut</span><span style="color:#323232;">() </span><span style="font-weight:bold;color:#a71d5d;">= </span><span style="color:#0086b3;">true</span><span style="color:#323232;">;
</span><span style="color:#323232;">}
</span></pre>
</td></tr></tbody></table>
<p>The behavior here is pretty subtle, so let's see the output and break down why we get this behavior.
The output from this program is:</p>
<pre><code>begin cancelling `b`
cancelling root future
cancelling `race` future
cancellation of `b` complete
</code></pre>
<p>The core of this program is that we race two futures (line 28), one that returns immediately (line 8), and one that never completes (line 13).
We've attached a bunch of cancellation handlers at various points so we can observe the behavior and the order that things happen in.</p>
<p>The cancellation handler on <code>b</code> is pretty complex, but the idea here is create a future that waits until some flag is set.
We wanted to simulate something that takes a little bit of time to complete, but not an unbounded amount, so that we can interrupt the cancellation.</p>
<p>So, we start running, the <code>async { 42 }</code> completes immediately and then <code>race</code> has to start cancelling <code>b</code>.
This shows up in the line <code>begin cancelling `b` </code>.
This cancellation does not complete, even though we poll a few more times, because no one has set <code>done</code> to true.</p>
<p>The next step is to trigger the second cancellation of <code>b</code>.
We do this by letting the executor go out of scope without completing, which means the destructor calls <code>poll_cancel</code> on the root task.
This is when we see <code>cancelling root future</code> appear.
This gets passed on to the <code>race</code> future because of the way we've desugared <code>await</code>, so we see the program print <code>cancelling `race` future</code>.
In the implementation of <code>race</code>, its <code>poll_cancel</code> method cancels any futures that have not either completed or been cancelled.
In our case, this means we call <code>poll_cancel</code> on <code>b</code> again, but this time the call chain originates in the executor's destructor rather than the normal execution of <code>race</code>.</p>
<p>Finally, since the <code>done</code> flag has been set, <code>b</code>'s cancellation can complete and we see it print out <code>cancellation of `b` complete</code>.</p>
<p>If we had instead supported <em>recursive cancellation</em>, we would have had the option of having <code>b</code>'s cancellation handler terminate early.
There are likely cases where both options would make sense, but here we've chosen to use idempotent cancellation semantics across the board.</p>
<h2 id="cancel-during-unwind">Cancel during Unwind<a class="header-anchor" href="#cancel-during-unwind">🔗</a></h2>
<p>This one is left as an exercise for the reader (or a future blog post here), but I don't see any fundamental reason why we can't do it.<sup class="footnote-reference"><a href="#unwind-cancel" id="fnref:unwind-cancel">14</a></sup>
The gist of the idea is that anywhere we call <code>poll</code>, we'd want to wrap that in <code>catch_unwind</code>.
If the <code>poll</code> function panics, we'd want to catch that, then call the future's <code>poll_cancel</code> method to completion, and then call <code>resume_unwind</code> to continue unwinding.</p>
<p>It will be annoying to have to do a <code>poll</code>, <code>catch_unwind</code>, <code>poll_cancel</code>, <code>resume_unwind</code> dance everywhere, but the basic idea should work.</p>
<p>There are other challenges though.
One is that the <code>poll_cancel</code> functions will need to be written to be aware of the fact that they might be called during unwinding, which means the internal state for the future might be inconsistent.</p>
<h1 id="evaluation">Evaluation<a class="header-anchor" href="#evaluation">🔗</a></h1>
<p>Writing this post gave me the chance to thoroughly explore this design.
I would say overall I think this design has enough shortcomings that I don't want to advocate it as the solution for async cancellation handlers.
I still think this is useful because the shortcomings can help us find a design with fewer, or at least more acceptable, compromises.
The fact that I've been able to implement this as a prototype means we can easily pivot and explore variations.</p>
<p>That said, I wouldn't have written so much about this design if I didn't think it had some merit.
So now I'd like to discuss what I see as some of the greatest strengths and shortcomings.</p>
<h2 id="strengths">Strengths<a class="header-anchor" href="#strengths">🔗</a></h2>
<p>In my mind, the biggest strength is that it feels like a relatively small extension to async Rust, but it still gives a lot of benefits.
It's basically one new method on the <code>Future</code> trait, as well as a minor change to the way <code>async</code> and <code>await</code> desugar.
We can provide a default implementation of <code>poll_cancel</code> which preserves the status quo semantics for cancellation and therefore makes the migration path pretty easy in most cases.
Of course, we're going to come back to this in the Weaknesses section because it's not all roses.</p>
<p>This design makes it clear what the responsibilities are for well-behaved executors (and executor-like things, like future combinators) to make sure cancellation behavior makes sense.</p>
<p>I think this design also works well with the requirement that futures are pinned.
For example, and alternate approach could be adding a method like <code>fn cancel(self) -&gt; impl Future&lt;Output = ()&gt;</code>.
The problem is that once a future has been pinned, you can't pass it as <code>self</code>.
Instead, the signature would have to be something like <code>fn cancel&lt;'a&gt;(self: Pin&lt;&amp;'a mut Self&gt;) -&gt; impl Future&lt;Output = () + 'a</code>, which I think is going to be annoying for executors to work with in practice.
Cancelling in place strikes me as significantly simpler.</p>
<p>All of the benefits I've talked about in this post are available without what strike me as significantly more extensive language changes.
For example, this gives us some way to run code on cancellation paths without needing complete support for <code>async Drop</code>.
Of course, this leads to significant shortcomings that we'll see in Weaknesses.
On the bright side, I think something like the <code>poll_cancel</code> API can serve as a compilation target for cancellation, the same way that poll is a compilation target for <code>await</code>.</p>
<h2 id="weaknesses">Weaknesses<a class="header-anchor" href="#weaknesses">🔗</a></h2>
<p>The weaknesses in this design range from what to me seems rather tolerable to some that I find completely unacceptable.</p>
<p>On the more tolerable end of the spectrum, there's the fact that this API feels a little fragile.
We have a requirement that once you call <code>poll_cancel</code> on a future you can never call <code>poll</code> again, but the compiler can't do anything to prevent you from doing that.
This kind of requirement isn't unprecedented though.
For example, with futures you already aren't supposed to call <code>poll</code> again after the future has completed, but the compiler doesn't stop you from doing that.
In both cases, we can mitigate this by treating <code>await</code> as the normal interface to <code>poll</code> and <code>poll_cancel</code> and guaranteeing that those generate correct code.
Calling <code>poll</code> and <code>poll_cancel</code> directly would then be considered an advanced use case, so we can tolerate more complex requirements there.<sup class="footnote-reference"><a href="#fuse-cancel" id="fnref:fuse-cancel">15</a></sup></p>
<p>I'm slightly more concerned about the migration path.
As a strength, I mentioned that the default impl of <code>poll_cancel</code> means without any additional action, futures will retain their present-day behavior.
In many cases, this is perfectly fine, but it's probably the wrong default for future combinators.
For example, suppose you were using an async IO crate that supported asynchronously cancelling operations in flight, but you put one of those futures behind an older version of <code>race</code> that did not yet support <code>poll_cancel</code>.
In this case, when the race future is cancelled, it would fall back on the default implementation, which says "ok, all good, nothing left to do," <em>without calling <code>poll_cancel</code> on the IO operation</em>.
The result would be that the programmer has to be extremely careful to make sure that everything in their call chain handles cancellation correctly.
Cancellation would be best effort, at best.
You definitely could not rely on this for safety!</p>
<p>One possible way to avoid this might be to introduce <code>poll_cancel</code> through a <code>CancellableFuture</code> trait instead.
Doing this in a way that's backwards-compatible would be tricky though.</p>
<p>Related to this shortcoming, <code>poll_cancel</code> puts a heavy burden on executor and future combinator authors.
It's already tricky to write a state machine that calls <code>poll</code>. Having to add <code>poll_cancel</code> calls to that state machine as well is going to be a lot of error-prone work.
We might be able to factor some of this work into common libraries that make it easier though.</p>
<p>But to me the most critical shortcoming of this design is that it it's easy to forget to cancel a future.
Fortunately, as long as your future is always behind an <code>await</code>, you should be okay.
On the other hand, there are common patterns that would now be error-prone.
For example, consider the following example with <code>FuturesUnordered</code>:</p>
<pre style="background-color:#ffffff;">
<span style="font-weight:bold;color:#a71d5d;">let mut</span><span style="color:#323232;"> futures </span><span style="font-weight:bold;color:#a71d5d;">= </span><span style="color:#323232;">FuturesUnordered::new();
</span><span style="color:#323232;">futures.</span><span style="color:#62a35c;">push</span><span style="color:#323232;">(async { </span><span style="color:#62a35c;">do_something</span><span style="color:#323232;">().await; });
</span><span style="color:#323232;">futures.</span><span style="color:#62a35c;">push</span><span style="color:#323232;">(async { </span><span style="color:#62a35c;">do_something_else</span><span style="color:#323232;">().await; });
</span><span style="color:#323232;">futures.</span><span style="color:#62a35c;">next</span><span style="color:#323232;">().await;
</span><span style="color:#62a35c;">drop</span><span style="color:#323232;">(futures);
</span></pre>
<p>Here we've added two futures to a <code>FuturesUnordered</code> collection.
When we call <code>next()</code>, it will poll both futures until one of them completes, and then the <code>next()</code> future will complete.
This means that <code>futures</code> is still holding on to a partially completed future.
But, when we <code>drop(futures)</code>, there's no way to run <code>poll_cancel</code> because <code>drop</code> must complete synchronously.
So, our only option right now is to just not cancel the future.</p>
<p>I suppose one way to work around this shortcoming is to try to argue that <code>FuturesUnordered</code> is a bad API.
Maybe I could redefine what we mean by <a href="https://blog.yoshuawuyts.com/tree-structured-concurrency/"><em>structured concurrency</em></a> to say that <code>FuturesUnordered</code> is unstructured and the cancellation mechanism we've described here only works for structured concurrency.
If I were to take this approach, our example would look more like this when using a redesigned <code>FuturesUnordered</code> collection:</p>
<pre style="background-color:#ffffff;">
<span style="color:#323232;">FuturesUnordered::with(async </span><span style="font-weight:bold;color:#a71d5d;">|</span><span style="color:#323232;">futures</span><span style="font-weight:bold;color:#a71d5d;">| </span><span style="color:#323232;">{
</span><span style="color:#323232;">    futures.</span><span style="color:#62a35c;">push</span><span style="color:#323232;">(async { </span><span style="color:#62a35c;">do_something</span><span style="color:#323232;">().await; });
</span><span style="color:#323232;">    futures.</span><span style="color:#62a35c;">push</span><span style="color:#323232;">(async { </span><span style="color:#62a35c;">do_something_else</span><span style="color:#323232;">().await; });
</span><span style="color:#323232;">    futures.</span><span style="color:#62a35c;">next</span><span style="color:#323232;">().await;    
</span><span style="color:#323232;">}).await;
</span></pre>
<p>This solves the problem by making it so that <code>FuturesUnordered::with</code> does no work until its awaited, so there is never any partially completed future that's not under an <code>await</code> point.
It's less than ideal for a few reasons though.
Stylistically, it adds more rightward drift.
But more importantly, this API makes it hard to put a <code>FuturesUnordered</code> in another data structure, which can be quite useful in many situations.
Plus, in my subjective opinion, the original version feels more Rusty.</p>
<p>Without a solution, I think this issue will make cancellation handlers so unreliable as to not be useful.
In fact, they will likely do more harm than good.
This leaves me convinced that we need some more general solution, like <code>async Drop</code>.
The key thing is to have some mechanism for the compiler to make sure, in an async function, that any values that need cancelled are cancelled.
To be honest, I'm a bit disappointed by this realization.
I haven't personally seen a design for <code>async Drop</code> that I love<sup class="footnote-reference"><a href="#implicit-await" id="fnref:implicit-await">16</a></sup>, so I was hoping that something like <code>poll_cancel</code> would give us most of the benefits of <code>async Drop</code> without having to wrestle with as many complex design issues.</p>
<p>That said, I think a design like <code>poll_cancel</code> complements a higher level feature like <code>async Drop</code>.
Even if we have a <code>async Drop</code>, we need to figure out how these get run and whether we can get the properties we want in order to build on them.
I think a variation on <code>poll_cancel</code> would give us a useful lower level target to build a more powerful feature like <code>async Drop</code> on top of.</p>
<h1 id="related-work">Related Work<a class="header-anchor" href="#related-work">🔗</a></h1>
<p>If you've been following this space for a while, the ideas I've discussed here probably sound very familiar.
I wanted to take the time to both acknowledge the work that's come before, but also highlight the ways in which my proposal here differs from earlier work.</p>
<p>One of the earliest versions I'm aware of is the (now abandoned) <a href="https://github.com/withoutboats/rfcs/blob/poll-drop-ready/text/0000-poll-drop-ready.md"><code>poll_drop_ready</code> RFC</a> from Boats.
One of the biggest differences is that the RFC focuses a lot on compiler-generated async drop glue to call <code>poll_drop_ready</code> and make sure things are cleaned up well, while I've left that completely out of scope for this post.
I appreciated the RFC's careful consideration of issues around pinning and fusing <code>poll_drop_ready</code>.
I've not really thought about these issues in my post, but I think we will need to if we move forward with this or a similar design.
I also appreciated that the RFC called out that the synchronous <code>drop</code> would still be called after <code>poll_drop_ready</code> returns <code>Ready(())</code>.
That feature was implicit in my design as well, but I think it is better to call it out.
The most important distinction, however, is that I have focused mainly on cancellation semantics in this post (that is, what if a future is not polled to completion?), while it seems that <code>poll_drop_ready</code> is called as part of the parent future completing normally through <code>poll</code>.
In other words, it seems executors are not intended to call <code>poll_drop_ready</code> directly.
This has some implications on when the programmer can assume <code>poll_cancel</code>/<code>poll_drop_ready</code> will be called.</p>
<p>There was another <a href="https://internals.rust-lang.org/t/add-poll-cancel-to-future/16050">proposal on IRLO</a> to add <code>poll_cancel</code> to the <code>Future</code> trait that is syntactically exactly the same as I've described here.
The semantics look essentially the same as I've describe here as well, with perhaps some minor variations.
For example, in my design I've imagined you do not have to call <code>poll_cancel</code> on a future that's never been polled.<sup class="footnote-reference"><a href="#cancel-unpolled" id="fnref:cancel-unpolled">17</a></sup>
I think the guarantees on the contract in the IRLO post are stronger than I was hoping we'd need here---I imagined we could get away with saying something like "a <em>well-behaved</em> executor should..." rather than "you must."
In particular, I didn't have the requirement that "A polled future may not be dropped without <code>poll_cancel</code> returning ready," and instead imagined such a thing would be impolite but not illegal.
I think the biggest contribution I've made in my post is showing how to adjust the desugaring of <code>async</code> and <code>await</code> to work with <code>poll_cancel</code>, giving us an answer to how "to generate a state machine that can keep track of a future in mid cancellation as a possible state."</p>
<p>Another excellent contribution in this area is <a href="https://gist.github.com/Matthias247/354941ebcc4d2270d07ff0c6bf066c64">A case for CancellationTokens</a>.
One of the things I really like about the post is the review of the major options in this space, including <code>request_cancellation</code>, <code>poll_cancel</code>, <code>async fn cancel</code> and cancellation tokens.
If you haven't read it yet, that section alone is worth the read!
The main idea behind cancellation tokens is to have some bit of state that's carried along the await chain and futures can check whether they've been cancelled and activate the correct behavior in that case.
It has some nice benefits around composability, and seems to be better at traversing code that is not cancellation-aware, which is a major shortcoming of <code>poll_cancel</code> as I've describe it here.
One thing I find interesting is that although on the surface cancellation tokens and <code>poll_cancel</code> look like extremely different mechanisms, they have more in common than it appears.
For example, the extra <code>is_cancelled</code> flag we added in the <code>async</code> and <code>await</code> desugaring looks an awful lot like a cancellation token.
I think it'd be worth exploring this connection in more depth.</p>
<p>The last idea I want to explore is <code>request_cancellation</code>, which seems to have been first introduced in some <a href="https://hackmd.io/uAI0q9ZiSwyUkmGQQkyXXg?view#New-async-trait">early async vision notes</a> by Niko Matsakis.
This is framed as a replacement <code>Future</code> trait called <code>Async</code> which includes a <code>request_cancellation</code> method.
The idea is that after calling <code>request_cancellation</code> on a future subsequent calls to <code>poll</code> would proceed along the cancellation path rather than the normal execution path.
This has a couple of strengths.
It avoids the possibility of calling <code>poll</code> after calling <code>poll_cancel</code>.
More importantly though, <code>request_cancellation</code> can be used to support recursive cancellation.
After writing this post, I'm actually pretty excited about <code>request_cancellation</code> because it seems strictly more powerful than <code>poll_cancel</code>.</p>
<h1 id="conclusion">Conclusion<a class="header-anchor" href="#conclusion">🔗</a></h1>
<p>In this post we've made an in-depth exploration of how a <code>poll_cancel</code> API would support cancellation handlers in Rust.
The design includes a prototype implementation which allows us to write real programs to get a feel how cancellation behaves.
In the course of doing this, we realized that <code>poll_cancel</code> has some significant shortcomings and is probably not the best mechanism for cancellation handlers going forward.
But, we also see promise for related proposals to address the specific shortcomings we've identified.</p>
<hr />
<div class="footnote-definition" id="executor"><sup class="footnote-definition-label">1</sup>
<p>I'm using executor broadly here to basically mean "any code that calls <code>poll</code> on futures directly." This obviously includes async runtimes, but also includes many <a href="https://docs.rs/futures-concurrency/latest/futures_concurrency/">future combinators</a> like <code>race</code> or <code>join</code>.<a href="#fnref:executor" class="footnote-backref">↩</a></p>
</div>
<div class="footnote-definition" id="magic"><sup class="footnote-definition-label">2</sup>
<p>This is a little bit of a lie. They desugar into generators and <code>yield</code> expressions, which do involve a fair amount of compiler magic to implement. The key thing here is that we don't have to do much additional magic if we can rely on the compiler to give us support for generators.<a href="#fnref:magic" class="footnote-backref">↩</a></p>
</div>
<div class="footnote-definition" id="await-macro"><sup class="footnote-definition-label">3</sup>
<p>Indeed, in the early days of async Rust, <code>await!</code> was in fact implemented as a macro.<a href="#fnref:await-macro" class="footnote-backref">↩</a></p>
</div>
<div class="footnote-definition" id="panic"><sup class="footnote-definition-label">4</sup>
<p>Well, not quite. Anything can panic, which you can treat as another final state for a function.<a href="#fnref:panic" class="footnote-backref">↩</a></p>
</div>
<div class="footnote-definition" id="option"><sup class="footnote-definition-label">5</sup>
<p><code>Option</code> would work just as well.<a href="#fnref:option" class="footnote-backref">↩</a></p>
</div>
<div class="footnote-definition" id="async-coroutines"><sup class="footnote-definition-label">6</sup>
<p><a href="https://github.com/traviscross/asyncawait-coroutines/blob/master/src/lib.rs">TC has also shown</a> that we can emulate coroutines using <code>async</code>/<code>await</code>, so it's probably even possible to do all of this on stable Rust.<a href="#fnref:async-coroutines" class="footnote-backref">↩</a></p>
</div>
<div class="footnote-definition" id="cancelled-context"><sup class="footnote-definition-label">7</sup>
<p>We could also add a <code>Context::is_cancelled()</code> method and just pass one parameter. There are a lot of ways to plumb this around.<a href="#fnref:cancelled-context" class="footnote-backref">↩</a></p>
</div>
<div class="footnote-definition" id="pseudo-code"><sup class="footnote-definition-label">8</sup>
<p>This is pseudo code. I'm assuming the pinning stuff just works. Also, my actual implementation had some <code>transmute</code> crimes that I've left out here for clarity.<a href="#fnref:pseudo-code" class="footnote-backref">↩</a></p>
</div>
<div class="footnote-definition" id="complete-after-cancel"><sup class="footnote-definition-label">9</sup>
<p>This "complete after cancel" case is one that could reasonably happen. For example, maybe you sent a request to a server, started to cancel it, but before you could the server sent back a response saying the request was completed. One possible behavior is to just drop the return value and say the cancellation was actually successful. In code this would mean replacing the <code>panic!("future completed after being cancelled")</code> line with <code>Poll::Ready(())</code>. The design in this post doesn't do this, but futures themselves are empowered to handle this case however they see fit.<a href="#fnref:complete-after-cancel" class="footnote-backref">↩</a></p>
</div>
<div class="footnote-definition" id="scheme"><sup class="footnote-definition-label">10</sup>
<p>If this were Scheme, I'd call this macro something like <code>await/c</code> or <code>await/cancel</code>, but Rust doesn't let us use <code>/</code> in identifiers.<a href="#fnref:scheme" class="footnote-backref">↩</a></p>
</div>
<div class="footnote-definition" id="defer-drop"><sup class="footnote-definition-label">11</sup>
<p>Incidentally, I'm also not entirely in love with <code>defer {}</code> and <code>async Drop</code>, but I think <code>async Drop</code> in particular solves a lot of problems I don't know how to solve otherwise.<a href="#fnref:defer-drop" class="footnote-backref">↩</a></p>
</div>
<div class="footnote-definition" id="mini-executor"><sup class="footnote-definition-label">12</sup>
<p>Sometimes I also find it helpful to think of combinators as mini executors, since combinators and executors both call poll functions on other futures directly.<a href="#fnref:mini-executor" class="footnote-backref">↩</a></p>
</div>
<div class="footnote-definition" id="recursive-cancellation"><sup class="footnote-definition-label">13</sup>
<p>I don't think it would take too much to extend this to support <em>recursive cancellation</em>, but that's left for another post or an exercise for the reader. I think they key thing is you need some way to tell how many times you've been cancelled. One way is to add a <code>depth</code> or <code>count</code> parameter to <code>poll_cancel</code>. Another is to have cancelling a future destroy the old future and create a new future that represents the cancellation of the old one, which could itself be cancelled.<a href="#fnref:recursive-cancellation" class="footnote-backref">↩</a></p>
</div>
<div class="footnote-definition" id="unwind-cancel"><sup class="footnote-definition-label">14</sup>
<p>Whether we <em>want</em> to do it is a fair question though.<a href="#fnref:unwind-cancel" class="footnote-backref">↩</a></p>
</div>
<div class="footnote-definition" id="fuse-cancel"><sup class="footnote-definition-label">15</sup>
<p>This is somewhat related to fusing futures and iterators. I haven't really touched on what happens if you call <code>poll_cancel</code> after the future is cancelled, but I think <a href="https://github.com/withoutboats/rfcs/blob/poll-drop-ready/text/0000-poll-drop-ready.md">Boats' earlier proposed RFC on <code>poll_drop_ready</code></a> makes a pretty good case that <code>poll_cancel</code> should require fused semantics -- that is, that you can call <code>poll_cancel</code> again after it completes and nothing bad happens.<a href="#fnref:fuse-cancel" class="footnote-backref">↩</a></p>
</div>
<div class="footnote-definition" id="implicit-await"><sup class="footnote-definition-label">16</sup>
<p>For example, I haven't seen a good way to run async destructors without introducing implicit await points. I like that right now we have the property that you can see anywhere an <code>async fn</code> might suspend by looking for <code>await</code>. Although, if I'm totally honest, this may not actually be that useful of a property.<a href="#fnref:implicit-await" class="footnote-backref">↩</a></p>
</div>
<div class="footnote-definition" id="cancel-unpolled"><sup class="footnote-definition-label">17</sup>
<p>The reason for this was to try to make it so we could get away with only having to deal with <code>poll_cancel</code> in the desugaring of <code>await</code>. Given the issue with <code>FuturesUnordered</code>, I don't think we can get away with only calling <code>poll_cancel</code> as part of <code>await</code> and will probably need some kind of compiler-generated drop glue cancellation path. Thus, it's probably simpler and better overall to have <code>poll_cancel</code> called even on futures that haven't been polled yet.<a href="#fnref:cancel-unpolled" class="footnote-backref">↩</a></p>
</div>
]]></content><author><name>Eric Holk</name></author><summary type="html"><![CDATA[<p>One of the items on our <a href="https://github.com/orgs/rust-lang/projects/28/views/1">Async 2027 Roadmap</a> is to come up with some kind of <a href="https://github.com/rust-lang/wg-async/issues/297">asynchronous cleanup mechanism</a>, like <code>async Drop</code>.
There are some tricky design questions to making this work well, and we need to start thinking about these now if we want to have something ready by 2027.</p>
<p>In this post, I'd like to explore a low level mechanism for how we might implement async cancellation.
The goal is to explore both how an async executor<sup class="footnote-reference"><a href="#executor" id="fnref:executor">1</a></sup> would interact with cancellation, as well as to make sure that this mechanism would support reasonable surface-level semantics.
You can think of this as a kind of compilation target for higher level features, similar to how the Rust compiler lowers <code>async fn</code> into coroutines.</p>
]]></summary></entry><entry><title type="html">Cancellation and Async State Machines</title><link href="https://theincredibleholk.org/blog/2023/11/08/cancellation-async-state-machines/" rel="alternate" type="text/html" title="Eric Holk"/><published>2023-11-08T00:00:00+00:00</published><updated>2023-11-08T00:00:00+00:00</updated><id>https://theincredibleholk.org/blog/2023/11/08/cancellation-async-state-machines/</id><content type="html" xml:base="https://theincredibleholk.org/blog/2023/11/08/cancellation-async-state-machines/"><![CDATA[<p>If you've been doing async for a while, you've probably heard someone say something like "the compiler takes an async function and converts it to a state machine."
I want to dive into this more, since we can think of cancellation as making the state machine more complex.
In this post, I'll show how to build a state machine for a simple async function.
Then we'll we'll see how the state machine changes if we want to be able to run async code during cancellation.
Finally, we'll explore some of the design space around cancellation, particularly what happens if a future that has been cancelled is cancelled again, and see how state machines can suggest several possibilities.</p>
<!-- MORE -->
<p>Let's use the program below as a running example.
In real life, this would probably return a <code>Result&lt;DataTable&gt;</code>, but I want to avoid the extra complexity around additional early exits.</p>
<pre style="background-color:#ffffff;">
<span style="color:#323232;">async </span><span style="font-weight:bold;color:#a71d5d;">fn </span><span style="font-weight:bold;color:#795da3;">load_data</span><span style="color:#323232;">(file: AsyncFile) -&gt; DataTable {
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">let mut</span><span style="color:#323232;"> data </span><span style="font-weight:bold;color:#a71d5d;">= </span><span style="color:#0086b3;">Vec</span><span style="color:#323232;">::new();
</span><span style="color:#323232;">    </span><span style="font-weight:bold;color:#a71d5d;">let</span><span style="color:#323232;"> result </span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#323232;"> file.</span><span style="color:#62a35c;">read_to_end</span><span style="color:#323232;">(</span><span style="font-weight:bold;color:#a71d5d;">&amp;mut</span><span style="color:#323232;"> data).await;
</span><span style="color:#323232;">    result.</span><span style="color:#62a35c;">unwrap</span><span style="color:#323232;">(); </span><span style="font-style:italic;color:#969896;">// We&#39;re ignoring proper error handling
</span><span style="color:#323232;">    </span><span style="color:#62a35c;">parse_data</span><span style="color:#323232;">(data)
</span><span style="color:#323232;">}
</span></pre>
<p>For an async function's state machine, states are made up of the the code between await points.
Or alternatively, you can think of await points as edges between states.
For this program, the state machine would look like this:</p>
<p><img src="/images/2023-11-08-basic-state-machine.svg" alt="A basic state machine diagram for the preceding code fragment" /></p>
<p>You might notice I pulled a bit of a fast one on you.
I said await points turn into edges in the state transition diagram, so we'd expect to see just one edge labeled <code>await</code>.
Instead, we have two edges labelled <code>await</code> and one without a label.
What's going on?</p>
<p>First, some conventions.
I realized it's helpful to see some of the traditional control flow in addition to suspension or await points.
I've represented these edges as a solid, unlabeled line.
These mean that control transfers from the previous state immediately to the second state without any suspension.
Our example is a relatively simple strait-line program so the actual control flow graph isn't particularly interesting but this will change a little when we look at cancellation.
The other edge we have in this graph is the <code>await</code> edge.
These edges are labeled <code>await</code> and are dotted lines to indicate that execution is interrupted---the future will suspend and give the executor the chance to switch to another future for a time.
Finally, I've introduced a couple of special states that do not exactly correspond to any code the user wrote.
These states are shown in orange.</p>
<p>Now let's turn our attention to why the diagram shows <em>two</em> await edges but the <code>await</code> keyword only shows up once in the program.
Every <code>async fn</code> has an implicit suspend point that represents the time between when the function is called and when is first polled.
In this diagram, I've represented this as an await edge going from the start state to the first line of the function.
In general, you don't have to worry about this hidden initial suspend point too much because async function calls are almost always immediately awaited.
In other words, it's more common to see <code>foo().await</code> instead of <code>let future = foo(); /* do some other stuff */; future.await</code>.</p>
<h1 id="cancellation">Cancellation<a class="header-anchor" href="#cancellation">🔗</a></h1>
<p>The state machine we've looked at so far does not do a good job of representing cancellation.
Let's try to extend it to do so.</p>
<p>Today in Rust cancellation simply means you stop polling the future, and instead it is dropped.
When dropping something like a closure or a future returned by an <code>async fn</code>, Rust needs to recursively drop the values store in (in other words, <em>captured by</em>) the closure or future.
Depending on what state the future is in when it is dropped, there are different values that need to be captured.
In our example, if we drop the future before we pull it, we only need to drop the <code>AsyncFile</code> that was passed in as a parameter.
On the other hand, if the future is dropped at the await point, we also need to drop the <code>Vec</code> that we read the file contents into.</p>
<p>We can add some extra states to our graph to illustrate this.</p>
<p><img src="/images/2023-11-08-uninterruptible-cancellation.svg" alt="A state machine diagram showing cancellation as it exists today, where cancellation cannot be interrupted once started" /></p>
<p>I've specifically called out <code>drop</code> along the cancellation path, but Rust also drops values during the normal exit path.
I've left the normal drops out for simplicity.</p>
<p>I like thinking of async functions this way because we can use it to make several observations about cancellation in Rust.
Many of these seem rather obvious, but they raise important requirements for designing a system that can handle cancellation well.</p>
<p><strong>Observation 1: Cancellation is a state change.</strong> When we cancel a future, it transitions from its normal running states to a cancellation path.
Currently this happens implicitly when a future is dropped, but in the future we will probably want a way to explicitly transition a future to its cancellation path.</p>
<p><strong>Observation 2: Async cancellation handlers<sup class="footnote-reference"><a href="#cancellation-handlers" id="fnref:cancellation-handlers">1</a></sup> require adding await points on the cancellation path.</strong> At the moment, cancelling futures is synchronous.
This shows up in the async state graph in the fact that there are no await edges on the cancellation path.
If we want to allow for cancellation handlers, we will need to add await points in the cancellation path.
This may be obvious, but this also implies we need a way to make sure executors continue to poll futures that have been cancelled.</p>
<p><strong>Observation 3: Cancellation is an alternate exit.</strong> An async function that has been cancelled does not exit through the normal return path.
From the perspective of an async function author, this shows up as the function not continuing to execute past an await point.
From a types standpoint, a function cannot exit normally in general because we may not yet have a value of the right type to return.
In our example we can see that the type of the function does not allow it to exit at the await point, because at that point we have not created a <code>DataTable</code> to return.
This observation has implications that will show up in the types of the API we eventually design for cancellation handlers.</p>
<h1 id="cancellation-cancellation">Cancellation Cancellation<a class="header-anchor" href="#cancellation-cancellation">🔗</a></h1>
<p>Another thing we can explore with a state graph is what behaviors are possible if a cancelled future is cancelled again.
One common way this could happen is if you have something like a <a href="https://docs.rs/futures-concurrency/latest/futures_concurrency/future/trait.Race.html"><code>race</code> combinator</a> that returns the value of the first future to complete and cancels the other one.
If the <code>race</code> combinator is itself cancelled while it was cancelling the slower sub-future, the slower sub-future would be cancelled twice.</p>
<p>FIXME: write out and explain a code example of this case.s</p>
<p>Let's look at this in the abstract with state machines.</p>
<p>There are a couple of possibilities for how to handle cancellation of cancellation.
I'll consider three of them, inspired by the <a href="https://en.wikipedia.org/wiki/Zero_one_infinity_rule">zero one infinity rule</a>.</p>
<h2 id="0-cancelling-during-cancellation-is-not-allowed">0. Cancelling during cancellation is not allowed<a class="header-anchor" href="#0-cancelling-during-cancellation-is-not-allowed">🔗</a></h2>
<p>Once we have support for cancellation handlers, it will definitely be possible to write code that leads to trying to cancel a cancellation.
The <code>race</code> example we mentioned earlier is one example.
So in this option, we would declare cancelling a cancellation to be an error.
We have some flexibility on what mechanism we'd use exactly, but I think the best option would be to panic.</p>
<p>I think in practice this option is not feasible.
Cancellation flows from top to bottom (e.g. an executor decides to terminate a task early and so runs the task's cancellation handler), but the higher levels do not know anything about the internal behavior of futures.
An executor that is cancelling a task does not know if one of the task's subfutures is trying to cancel a future already.</p>
<h2 id="1-cancelling-a-cancellation-is-idempotent">1. Cancelling a cancellation is <em>idempotent</em><a class="header-anchor" href="#1-cancelling-a-cancellation-is-idempotent">🔗</a></h2>
<p>In this version, cancelling an already-cancelled future is basically a no-op.
In state machines, it would look something like this:</p>
<p><img src="/images/2023-11-08-idempotent-cancellation.svg" alt="A possible model for cancelling a cancellation, where cancellation is idempotent" /></p>
<p>The key point here is that any of the cancel states have a cancellation edge that comes back to the same state.
In other words, cancelling once your future has already been cancelled means you stay in the same state and continue executing the cancellation handler before.</p>
<p>What does this mean in practice?
It essentially means you can trust that your cleanup code in a cancellation handler will run to completion.
Admittedly, this might take additional rules, like we may want to declare it to be undefined behavior to not poll a cancelled future to completion<sup class="footnote-reference"><a href="#unsafe" id="fnref:unsafe">2</a></sup>.
Scoped tasks would likely need this guarantee, but we could consider weaker ones, like that a "well-behaved" executor will poll cancelled futures to completion.
The "well-behaved" guarantee is roughly what we have today for <code>Drop</code>, so it might be similarly useful.</p>
<p>The downside is that this also means we can add cancellation behavior that can take arbitrarily or even infinitely long.<sup class="footnote-reference"><a href="#nonterminating-drop" id="fnref:nonterminating-drop">3</a></sup>
We might decide instead that cancellation means something like "request graceful shutdown" but then forcibly terminate a future if it takes too long.
For this we need <em>recursive cancellation</em>.</p>
<h2 id="inf-cancelling-a-cancellation-is-recursive">∞. Cancelling a cancellation is <em>recursive</em><a class="header-anchor" href="#inf-cancelling-a-cancellation-is-recursive">🔗</a></h2>
<p>In this version, canceling an already cancelled future would transfer us to a separate cancellation path.
That cancellation path could also be cancelled, and its cancellation could be cancelled, and so on.
In pictures, recursive cancellation looks like this:</p>
<p><img src="/images/2023-11-08-recursive-cancellation.svg" alt="An alternate approach to cancellation handlers that allows for recursive cancellation" /></p>
<p>While an infinite regress of cancellations might seem ridiculous, there are some cases where it might be useful.
There's also a nice regularity to it.<sup class="footnote-reference"><a href="#regularity" id="fnref:regularity">4</a></sup>
One class of problems where this might be useful are cases where you have optional cleanup work to do but you can cancel it if needed for a more prompt shutdown.
Of course, I'm not sure this is really all that useful in practice, and if you need it there might be other ways to do it.</p>
<p>More importantly, there are many cases where you absolutely do not want to cancel the cancellation.
For example, maybe you have a transaction future whose cancellation path rolls back the transaction.
You do not want to stop the rollback before it's complete, or else you've completely defeated the purpose of transactions.</p>
<p>That said, recursive cancellation appears to be strictly more powerful than idempotent cancellation because if you have recursive cancellation you should be able to implement idempotent cancellation where needed (basically, you just ignore the subsequent cancellation signals and stay in the same state you were in).</p>
<p>Seen this way, recursive cancellation gives us a lot of flexibility.
It means individual futures can implement either behavior, according to what best fits their needs.
The main thing the Rust language would need to do is design reasonable defaults and set expectations so people authoring futures can encapsulate their specialized behavior.</p>
<h1 id="conclusion">Conclusion<a class="header-anchor" href="#conclusion">🔗</a></h1>
<p>We've long talked about async functions as state machines, so in this post we looked at how you might draw a state transition diagram for async functions.
This gave us a way to play with cancellation and look at what various cancellation semantics might imply in terms of the shape of the state transition diagram.
I've found it really helpful to think about async cancellation this way, so I hope others find it useful as well!</p>
<p>This post was originally part of a larger post about implementing a prototype of async cancellation handlers.
The larger post was taking a long time and I felt like the content in this post was useful on its own so I wanted to go ahead and publish it.
While I no longer like to promise that a followup post is coming soon<sup class="footnote-reference"><a href="#coming-soon" id="fnref:coming-soon">5</a></sup>, I do have most of the longer post drafted so chances are good I will get it out soon.
Plus, I did commit to discussing it at the <a href="https://github.com/rust-lang/wg-async/issues/323">WG Async Reading Club</a> next week, so there is a little pressure on.</p>
<p>Anyway, please reach out if you have any thoughts or questions!</p>
<hr />
<div class="footnote-definition" id="cancellation-handlers"><sup class="footnote-definition-label">1</sup>
<p>I'm using "cancellation handlers" refer broadly to mechanisms to allow running async code on the cancellation path. This would likely be <code>async Drop</code>, I want to use a more general term to emphasize there are multiple possibilities here.<a href="#fnref:cancellation-handlers" class="footnote-backref">↩</a></p>
</div>
<div class="footnote-definition" id="unsafe"><sup class="footnote-definition-label">2</sup>
<p>This will require us to mark something <code>unsafe</code> somewhere.<a href="#fnref:unsafe" class="footnote-backref">↩</a></p>
</div>
<div class="footnote-definition" id="nonterminating-drop"><sup class="footnote-definition-label">3</sup>
<p>This is true of <code>drop</code> already today. I can write <code>fn drop(&amp;mut self) { loop {} }</code> and my program will hang when the destructor tries to run.<a href="#fnref:nonterminating-drop" class="footnote-backref">↩</a></p>
</div>
<div class="footnote-definition" id="regularity"><sup class="footnote-definition-label">4</sup>
<p>One of the things that bugs me about the idempotent version of cancellation is that you can call any future from either a normal execution or cancellation path, but in the cancellation path they effectively become uncancellable. It's not actually a problem, since not cancelling a future is always a choice your allowed to make, but the asymmetry still bothers me.<a href="#fnref:regularity" class="footnote-backref">↩</a></p>
</div>
<div class="footnote-definition" id="coming-soon"><sup class="footnote-definition-label">5</sup>
<p>I'm sure you'll find plenty of examples on my blog of posts I said were coming soon that did not, in fact, come soon, if they ever came at all.<a href="#fnref:coming-soon" class="footnote-backref">↩</a></p>
</div>
]]></content><author><name>Eric Holk</name></author><summary type="html"><![CDATA[<p>If you've been doing async for a while, you've probably heard someone say something like "the compiler takes an async function and converts it to a state machine."
I want to dive into this more, since we can think of cancellation as making the state machine more complex.
In this post, I'll show how to build a state machine for a simple async function.
Then we'll we'll see how the state machine changes if we want to be able to run async code during cancellation.
Finally, we'll explore some of the design space around cancellation, particularly what happens if a future that has been cancelled is cancelled again, and see how state machines can suggest several possibilities.</p>
]]></summary></entry></feed>