[HN Gopher] Cancellations in async Rust
       ___________________________________________________________________
        
       Cancellations in async Rust
        
       Author : todsacerdoti
       Score  : 229 points
       Date   : 2025-10-03 16:18 UTC (1 days ago)
        
 (HTM) web link (sunshowers.io)
 (TXT) w3m dump (sunshowers.io)
        
       | CaptainOfCoit wrote:
       | Less clickbaity title: Cancellations in async Rust.
       | 
       | It's really not about "cancelling async Rust" which is what I
       | expected, even if it didn't make much sense.
        
         | happytoexplain wrote:
         | As in the pop-culture concept of cancelling? That's what you
         | assumed the topic "cancelling async <language name>" was going
         | to be about??
         | 
         | Or am I missing context?
        
           | hansvm wrote:
           | That's what I assumed. I know languages have to handle
           | cancellations in async code, but Rust has had a fair amount
           | of drama over the years, and I assumed the title was accurate
           | and reflected that some drama was happening.
        
             | sunshowers wrote:
             | Appreciate the feedback here -- definitely don't want the
             | title to overshadow the work itself. Will keep this in mind
             | for next time.
        
           | thehamkercat wrote:
           | they probably assumed something like
           | some_running_async_task.cancel()
        
           | fulafel wrote:
           | Async in Rust isn't exactly universally loved, partly because
           | async Rust is perceived to spread progressively to Rust
           | libraries making it less optional to use. See eg
           | https://bitbashing.io/async-rust.html, "Async Rust Is A Bad
           | Language"
        
         | sunshowers wrote:
         | As the author of the talk/blog post, I was definitely going for
         | a bit of a moral valence in the title, in the sense that future
         | cancellations are very hard to reason about and what I call the
         | least Rusty part of Rust. But it admittedly is a bit clickbaity
         | too.
        
           | acedTrex wrote:
           | I initially skipped reading it because i thought it was
           | another drama post about maintainers a la all the nixos stuff
           | lately.
        
             | bigiain wrote:
             | To balance the universe a bit, I read it expecting a drama
             | post - then read it right through because it was at least
             | as interesting as the drama post I'd expected. I also
             | discovered Oxide through this, which looks interesting,
             | except for the complete lack of pricing I can find on their
             | site - which puts in my head as probably in the "If you
             | need to ask the price you can't afford it" category...
        
               | jgord wrote:
               | I think Oxide should be renting out time on their
               | hardware racks, as well as selling them to big orgs.
               | 
               | Oxide looks to be superb engineering up and down the
               | whole stack, and if it drives more rust code into linux
               | all the better.
               | 
               | Now that linode has been consumed by Akamai, we need an
               | alternative.
        
         | binary132 wrote:
         | if only
        
           | zackmorris wrote:
           | +1 this.
           | 
           | IMHO async is an anti-pattern, and probably the final straw
           | that will prevent me from ever finishing learning Rust. Once
           | one learns pass-by-value and copy-on-write semantics
           | (Clojure, PHP arrays), the world starts looking like a
           | spreadsheet instead of spaghetti code. I feel that a Rust-
           | like language could be built with no borrow checker, simply
           | by allocating twice the memory. Since that gets ever-less
           | expensive, I'm just not willing to die on the hill of
           | efficiency anymore. I predict that someday Rust will be
           | relegated to porting scripting languages to a bare-metal
           | runtime, but will not be recommended for new work.
           | 
           | That said, I think that Rust would make a great teaching tool
           | in an academic setting, as the epitome of imperative
           | languages. Maybe something great will come of it, like Swift
           | from Objective-C or Kotlin from Java. And having grown up on
           | C++, I have a soft spot in my heart for solving the hard
           | problems in the fastest way possible. Maybe a voxel game in
           | Rust, I dunno.
        
             | sunshowers wrote:
             | Author here -- I'd recommend reading my blog post about how
             | cargo-nextest uses Tokio + async Rust to handle very
             | complex state machines:
             | https://sunshowers.io/posts/nextest-and-tokio/
        
             | sertsa wrote:
             | There is a voxel game in Rust, btw: https://veloren.net/
        
             | wongarsu wrote:
             | The rust ecosystem is very invested into making every
             | library that touches the network async. But if the program
             | you are writing doesn't touch the network you don't have to
             | think about async. Or you can banish network code onto one
             | thread with an async runtime, and communicate via flume
             | queues/channels with it from normal threaded code running
             | in another thread
        
               | bigstrat2003 wrote:
               | > The rust ecosystem is very invested into making every
               | library that touches the network async.
               | 
               | Right, and that is one of the absolute worst things about
               | the Rust ecosystem. Most programs don't benefit from
               | async, and should use plain old threads because they are
               | _much_ easier to work with.
        
               | sunshowers wrote:
               | There is a very reasonable argument that an entire
               | language feature shouldn't be oriented towards making
               | high-complexity state machines easy to write, since
               | they're relatively rare in production. But speakingly
               | purely selfishly, I'm happy I can write something like
               | cargo-nextest using async Rust in a bug-free manner.
        
             | xmodem wrote:
             | In your view, which languages / ecosystems have a better
             | general approach for handling task cancellations than async
             | rust?
        
               | GhosT078 wrote:
               | Ada has very well thought out and proven tasking
               | features, including clean methods of task cancellation.
        
               | neillyons wrote:
               | Elixir via the Task module
               | https://hexdocs.pm/elixir/Task.html
        
             | vlovich123 wrote:
             | > Since that gets ever-less expensive,
             | 
             | That kind of thinking made sense in the 90s when things
             | followed Moore's law. But DRAM was one of the first things
             | to fail to keep up:
             | https://ourworldindata.org/grapher/historical-cost-of-
             | comput... and barely gets cheaper anymore. Thats why mobile
             | phones still only have 16gb of memory despite having 4gib a
             | decade ago.
             | 
             | And there's all sorts of problems that Rust doesn't
             | necessarily make a great fit for. But Rust's target
             | marketplace is where you'd otherwise use a low level
             | language like C or C++. If you can just heap allocate
             | everything and aggressively create copies all over the
             | place, then why would you ever use those languages in the
             | first place.
             | 
             | And for what it's worth Rust is finding a lot of success
             | even replacing all the tooling in other language ecosystems
             | like Ruby, Python, and JS precisely because the tools in
             | those ecosystems written in the native language end up
             | being horribly slow. And memory allocation and randomly
             | deep copying arrays are the kinds of things that add up and
             | make things slow (in addition to GC pauses, slow startups,
             | interpreter costs etc).
             | 
             | And you can always choose not to do async in Rust although
             | personally I'm a huge fan as it makes it really clear where
             | you have sprinkled in I/O in places you shouldn't have.
        
               | koito17 wrote:
               | Before adopting Rust, I also found it silly for high-
               | level tasks where e.g. Clojure or Java would suffice.
               | However, the _results_ of using Rust changed my mind.
               | 
               | I used to write web backends in Clojure, and justified it
               | with the fact that the JVM has some of the best profiling
               | tools available (I still believe this), and the JVM
               | itself exposes lots of knobs to not only fine-tune the
               | GC, but even choose a GC! (This cannot be understated;
               | garbage collectors tend to be deeply integrated into a
               | language's runtime, and it's amazing to me that the Java
               | platform manages to ship several garbage collectors, each
               | of which are optimal in their own specific situations).
               | 
               | After rewriting an NLP-heavy web app in Rust, I saw
               | massive performance gains over the original Clojure
               | version, even though both aggressively copy data and the
               | Rust version is full of atomic refcounts (atomic
               | refcounting is not the fastest GC out there...)
               | 
               | The binary emitted by rustc is also much smaller. ~10 MB
               | static binary vs. GraalVM's ~80 MB native images (and
               | longer build times, since classpath analysis and
               | reflection scanning require a lot of work)
               | 
               | What surprised me the most is how high-level Rust feels
               | in practice. I can use pattern matching, async/await,
               | functional programming idioms, etc., and it ends up being
               | fast anyway. Coming from Clojure, Rust syntax trying its
               | best to be expression-oriented is a key differentiator
               | from other languages in its target domain (notably, C++).
               | I sometimes miss TypeScript's anonymous enums, but Rust's
               | type system can express a lot of of runtime behavior, and
               | it's partly why many jokingly state "if it compiles, it's
               | likely correct". Then there's the little things, like how
               | Rust's Futures don't immediately start in the background.
               | In contrast, JavaScript Promises are immediately pushed
               | to a microtask queue, so cancelling a Promise is
               | impossible by design.
               | 
               | Overall, it's the little things like this -- and the
               | toolchain (cargo, clippy, rustfmt) -- that have kept me
               | using Rust. I can write high-level code and still compile
               | down to a ~5 MB binary and outperform idiomatic code in
               | other languages I'm familiar with (e.g. Clojure, Java,
               | and TypeScript).
        
               | sunshowers wrote:
               | Speaking personally, that is what first attracted me to
               | Rust -- that you can write high-level idiomatic code and
               | still get roughly optimal performance.
        
               | rafram wrote:
               | It isn't as dramatic a decrease as other types of
               | storage, but $4,000 to $1,000 per terabyte in a decade is
               | still a big drop.
        
             | airstrike wrote:
             | This reads hella uninformed
        
             | 63stack wrote:
             | By allocating twice the memory of ...?
        
               | SV_BubbleTime wrote:
               | Everything, everywhere, all the time! It's so simple, why
               | has no one ever thought of just increasing a finite
               | resource!?
        
             | dlahoda wrote:
             | lean4.
             | 
             | it analyses code. if it finds raii/linearity/single-
             | ownership, it does exactly like rust mem mgmt.
             | 
             | but if it js not, it does rc.
             | 
             | so it does what rust, but automagically without polluting
             | code.
             | 
             | so cow or pbw or 2mem are not only options to improve rust.
        
           | moggers123 wrote:
           | I just don't really know where the ecosystem is going with
           | async these days. I see a lot of changes in the language,
           | many of which seem more complex than are typical for the
           | justification, some of which have broader utility but
           | generally wouldn't be done if it weren't for them being
           | necessary for async... A hydra of complexity and honestly,
           | where does it end? When will async be "solved"? What will the
           | language look like when it is? Is it really all justified?
           | Did we know that the road would be this long when we started
           | it? For me, you could resolve this complexity by just letting
           | me flip a switch and have things operate in a really basic
           | blocking mode with no state machine or runtime and I'll sling
           | this stuff on a thread if I have to, the way I used to. My
           | use cases never needed this. I can get by just fine solving
           | the circumstances I would need something like this with a
           | purpose build mechanism, not a language native feature trying
           | to solve all the different flavors of asynchronous problems I
           | have + a million I don't.
           | 
           | of course... Its obviously not as simple as "just give me a
           | way to turn it off", but more importantly, I just don't see
           | this concern being addressed by the Powers That Be. Am I just
           | not looking hard enough? Did I miss the rust blog post titled
           | "hey - so you didn't want to use async but the libraries that
           | you did want to use ship with async so you're up shit creek..
           | Here's what our plan for that is"?
           | 
           | I'm sorry. I generally lurk because I don't consider myself
           | up to the caliber of others on this website, but nonetheless
           | the few posts I make do end up being about async because it
           | does make me feel quite hopeless at times. Hopefully someone
           | can look passed my
           | ignorance/incompetence/selfishness/immaturity and tell me its
           | all going to be okay.
        
         | benatkin wrote:
         | It made sense to me, because I imagine a thread or coroutine as
         | something that runs code as though it were interpreting
         | something like psuedocode, whether it's doing that or not. So
         | from my point of view an instance of async Rust is being
         | cancelled - not the feature of the Rust project, but instances
         | of code.
         | 
         | This abstraction has served me well and facilitates stepping
         | through code in a debugger, though I jump out of thinking it at
         | that level when I need to think of it at a lower level.
        
       | bryanlarsen wrote:
       | Timely! Was grumbling about this today as I added a "this
       | function is cancel safe" to a new function's doc comment.
       | 
       | I really hope we get async drop soon.
        
         | 0x1ceb00da wrote:
         | I'm curious. Can you talk a little about that function?
        
           | bryanlarsen wrote:
           | Most common scenario in article: select!. I split out a "wait
           | for X to be ready" from "X" so that the former could be on
           | the left side of a select ARM, and the rest on the right
           | side.
        
       | Panzerschrek wrote:
       | One should always keep in mind that _await_ is always a potential
       | return point. So, using _await_ between two actions which always
       | should be performed together should be avoided.
        
         | Spivak wrote:
         | That... seems bad? Like I guess it is what it is and you just
         | have to deal with it but what if your "critical section" has
         | two await calls? The code can be paused between them but it's
         | such that it must eventually resume. Say making a change in the
         | database and emitting an audit edit for that change. Is your
         | only option to either not do that or put a big do not cancel
         | sign on the function docs?
        
           | sunshowers wrote:
           | In general I think people end up gravitating towards using
           | message passing or the actor model for this.
        
           | diarrhea wrote:
           | A DB action and audit emission have to run transactionally
           | anyway.
           | 
           | So on cancellation, the transaction times out and nothing is
           | written. Bad but safe.
           | 
           | The problem is the same on other platforms. For example, what
           | if writing to the DB throws an exception if you're on Python?
           | Your app just dies, the transaction times out. Unfortunate
           | but safe.
           | 
           | If it does not run transactionally you have a problem in any
           | execution scenario.
        
             | sunshowers wrote:
             | So, regarding transactions, absolutely you can throw them
             | away on cancellation. But there's an interesting wrinkle
             | here: if you use a connection pool like most users, and you
             | were going to do the ROLLBACK at the end of your future on
             | error, then that ROLLBACK wouldn't run it the future is
             | cancelled! Then future operations reusing the same
             | connection would be stuck in transaction la-la land.
             | 
             | (This is related to the fact that Rust doesn't have async
             | drop -- you can't run async code on drop, other than
             | spawning a new task to do the cleanup.)
             | 
             | This is prong 3 of my cancel correctness framework (that
             | the cancellation violates a system property, in this case a
             | cleanup property.) The solution here is to ensure the
             | connection is in a pristine state before handing it out the
             | next time it's used.
        
           | setr wrote:
           | Even if you guaranteed the calling code would always
           | logically continue running the function till completion, you
           | wouldn't have the guarantee the code would actually resume --
           | eg the program crashes between the two calls, network dies,
           | etc.
           | 
           | If you want to tie multiple actions together as an atomic
           | unit, you need the other side to have some concept of
           | transactions; -- and you need to utilize it.
        
         | cogman10 wrote:
         | Wait, how does this work in practice?
         | 
         | Let's say my code looks like this                   async fn
         | a() {           b().await         }              async fn b() {
         | c().await           d().await         }              async fn
         | c() {         }              async fn d() {         }
         | 
         | Where does an issue occur which causes `d` to not to be called?
         | Is it some sort of cancellation in c? Or some upstream action
         | in a?
        
           | cogman10 wrote:
           | Ah, I see it now in the article. I just missed it.
           | 
           | `d` not being called would happen because of actions in `a`.
           | 
           | If `a` were rewritten as                   async fn a() {
           | try_join!(b(), c(), d())         }
           | 
           | Then if `c` ends up failing in the try_join then process on
           | `b` will be halted and thus the `d` in `b` won't be executed.
        
             | alfiedotwtf wrote:
             | Maybe I'm thick, but I'm not seeing what is the problem in
             | your first codeblock?
        
               | cogman10 wrote:
               | There's nothing wrong in my first comment, it's the
               | second that clarifies adding a `try_join` at the top of
               | the stack can break things below (which is what I was
               | trying to figure out in my initial comment).
               | 
               | Because rust is ultimately constructing a state machine
               | which is ran by the caller, the execution of that state
               | machine can be interrupted or partially executed at any
               | of the `await` points. Or more accurately the caller can
               | simply not advance the state machine.
               | 
               | So, the `try_join` macro can start work on the various
               | functions and if any of them fail, the others are
               | ultimately cancelled. Which can happen before those
               | functions finish fully executing.
               | 
               | This is particularly bad if there's a partial state
               | change.
               | 
               | I'm not entirely sure what that means for memory
               | allocation.
        
       | CodeBrad wrote:
       | This was one of my favorite talks from RustConf this year! The
       | distinction between cancel safety and cancel correctness is
       | really helpful.
       | 
       | Glad to see it converted to a blog post. Talks are great, but
       | blogs are much easier to share and reference.
        
         | sunshowers wrote:
         | Thanks! I definitely prefer reading blog posts over watching
         | talks as well.
        
         | pornel wrote:
         | "Cancel correctness" makes a lot of sense, because it puts the
         | cancellation in some context.
         | 
         | I don't like the "cancel safety" term. Not only it's unrelated
         | to the Rust's concept of safety, it's also unnecessarily
         | judgemental.
         | 
         | Safe/unsafe implies there's a better or worse behavior, but
         | what is desirable for cancellation to do is highly context-
         | dependent.
         | 
         | Futures awaiting spawned tasks are called "cancellation safe",
         | because they _won 't_ stop the task when dropped. But that's
         | not an inherently safe behavior - leaving tasks running after
         | their spawner has been cancelled could be a bug: piling up work
         | that won't be used, and even interfering with the rest of the
         | program by keeping locks locked or ports used. OTOH a spawn
         | handle that stops the task when dropped would be called
         | "cancellation unsafe", despite being a very useful construct
         | specifically for propagating cleanup to dependent tasks.
        
       | Matthias247 wrote:
       | Some other material that has been written by me on that topic:
       | 
       | - Proposal from 2020 about async functions which are forced to
       | run to completion (and thereby would use graceful cancellation if
       | necessary). Quite old, but I still feel that no better idea has
       | come up so far. https://github.com/Matthias247/rfcs/pull/1
       | 
       | - Proposal for unified cancellation between sync and async Rust
       | ("A case for CancellationTokens" -
       | https://gist.github.com/Matthias247/354941ebcc4d2270d07ff0c6...)
       | 
       | - Exploration of an implementation of the above:
       | https://github.com/Matthias247/min_cancel_token
        
       | ossopite wrote:
       | I think the send/recv with a timeout example is very interesting,
       | because in a language where futures start running immediately
       | without being polled, I think the situation is likely to be the
       | opposite way around. send with a timeout is probably safe (you
       | may still send if the timeout happened, which you might be sad
       | about, but the message isn't lost), while recv with a timeout is
       | probably unsafe, because you might read the message out of the
       | channel but then discard it because you selected the timeout
       | completion instead. And the fix is similar, you want to select
       | either the timeout or 'something is available' from the channel,
       | and if you select the latter you can peek to get the available
       | data.
        
         | sunshowers wrote:
         | Thanks, that is a great point.
        
         | lionkor wrote:
         | Isn't this exactly what cancellation-safety is all about?
        
       | Animats wrote:
       | In the initial example, it's not clear what the desired behavior
       | is. If the queue is full, the basic options are drop something,
       | block and wait, or panic. Timing out on a block is usually
       | deadlock detection. He writes "It turns out that this code is
       | often incorrect, because not all messages make their way to the
       | channel." Well, yes. You're out of resources. Now what?
       | 
       | What's he trying to do? Get a clean program shutdown? That's
       | moderately difficult in threaded programs, and async has
       | problems, too. The use case here is unclear.
       | 
       | The real use cases involve when you're sending messages back and
       | forth to a remote site, and the remote site goes away. Now you
       | need to dispose of the state on your end.
        
         | AceJohnny2 wrote:
         | > _He writes_
         | 
         | They go by they/she
         | 
         | https://sunshowers.io/about/
        
         | sunshowers wrote:
         | Ideally, what you would like to do is buffer up the message
         | until there's space in the channel. I cover this later in the
         | talk under "What can be done".
        
           | Animats wrote:
           | The double-loop thing effectively creates a blocking
           | operation. Something you can do directly. Why all the
           | complexity?
        
             | sunshowers wrote:
             | Agreed that in the narrow case of a timeout it doesn't buy
             | you much (and things like network sockets often let you do
             | timeouts in synchronous code). But often you do want the
             | power to do selects and more complex state machines. I
             | wrote a blog post a couple years ago talking about why a
             | project I'm the author of, cargo-nextest, switched from
             | sync Rust to async. https://sunshowers.io/posts/nextest-
             | and-tokio/
             | 
             | To this day I'm not aware of a better way to express what's
             | become a set of increasingly complex state machines (the
             | most recent improvement being to make the state machines
             | responsive to user input). Nextest's runner loop is
             | structured mostly like a GUI event loop, but without
             | explicit state machines. It's quite nice being able to
             | write code that's this complex in a bug-free manner.
        
           | ajross wrote:
           | Is that ideal, though? I mean, the channel _is_ the buffer.
           | If you need more buffer, it should have been bigger to start
           | with. Generally this reflects a resource exhaustion failure,
           | which you don 't handle by adding code. Fix the resource
           | allocation.
        
             | sunshowers wrote:
             | It depends on how tolerant you are to losing messages under
             | backpressure. In some cases at work we set a large channel
             | size, and then panic if it's exceeded.
        
         | leoedin wrote:
         | It's in the example isn't it? The example is logging "No space
         | for 5 seconds". It's just a helpful diagnostic that subtly
         | turned into data loss.
         | 
         | Maybe it's a bit contrived, but it's also the kind of code
         | you'd sprinkle through your system in response to "nothing
         | seems to be happening and I don't know why".
        
           | sunshowers wrote:
           | It's definitely a bit contrived, but to me it's also
           | emblematic of the issues with async Rust.
           | 
           | The note on mpsc::Sender::send losing the message on drop [1]
           | was actually added by me [2], after I wrote the Oxide RFD on
           | cancellations [3] that this talk is a distilled form of. So
           | even the great folks on the Tokio project hadn't documented
           | this particular landmine.
           | 
           | [1] https://docs.rs/tokio/latest/tokio/sync/mpsc/struct.Sende
           | r.h...
           | 
           | [2] https://github.com/tokio-rs/tokio/pull/5947
           | 
           | [3] https://rfd.shared.oxide.computer/rfd/0400
        
       | arifalkner wrote:
       | Great talk! One thing that would have been nice to call out for
       | n00bs like myself is how in SOP Futures can't cancelled. I knew
       | that .await took ownership of the future so that drop() could not
       | be called on it, so given how futures are lazy, it wasn't clear
       | to me how to cancel a future after .await had been called. I
       | later researched how select! and Abortable() did this, but could
       | be nice to include a callout in the beginning of your talk if you
       | ever do it again. Otherwise, nice work!
        
         | sunshowers wrote:
         | Thanks! What does SOP mean in this context?
        
       | alembic_fumes wrote:
       | I'm not understanding what the supposed problem with these
       | futures getting cancelled is. Since futures are not tasks, as the
       | post itself acknowledges, does it not logically follow that one
       | should not expect futures to complete if the future is not driven
       | to completion, for one reason or another? What else could even be
       | expected to happen?
       | 
       | The examples presented for "cancel unsafe" futures seem to me
       | like the root of the problem is some sort of misalignment of
       | expectations to the reality:
       | 
       | Example 1: one future cancelled on error in the other
       | 
       | let res = tokio::try_join!( do_stuff_async(), more_async_work(),
       | );
       | 
       | Example 2: data not written out on cancellation
       | 
       | let buffer: &[u8] = /* ... */; writer.write_all(buffer)?;
       | 
       | Both of these cases are claimed to not be cancel-safe, because
       | the work gets interrupted and so not driven to completion. But
       | again, what else is supposed to happen? If you want the work to
       | finish regardless of the async context being cancelled, then
       | don't put it in the same async context but spawn a task instead.
       | 
       | I feel like I must be missing something obvious that keeps me
       | from understanding the author's issue here. I thought work
       | getting dropped on cancellation is exactly how futures are
       | supposed to work. What's the nuance that I'm missing?
        
         | sunshowers wrote:
         | You're absolutely right! The problem is that this has
         | introduced many bugs in our experience at Oxide. If you've
         | already fully internalized the idea that futures are passive
         | and can be cancelled at any await point, the talk is just a
         | bunch of details.
        
           | alembic_fumes wrote:
           | I see. Do you suppose that the origin of these bugs is more
           | about the difficulty of reasoning about the execution of deep
           | async stacks, or does it come down to the developers holding
           | an incorrect mental model of the Rust futures in their minds?
           | 
           | I am asking because I've noticed that many developers with
           | previous experience from "task-based" languages (specifically
           | the JS/TS world) tend to grasp the basics of Rust async
           | quickly enough, but then run into expectation-misalignment
           | related problems similar to the examples that you used in
           | your post. That in turn has made want to understand whether
           | it is the Rust futures that are themselves difficult or
           | strange, or whether it's a case of the Rust futures appearing
           | simple and familiar, even though they are completely
           | different in very subtle ways. I suppose that it's a
           | combination of both.
        
             | sunshowers wrote:
             | Yeah, it's a combination of both in my experience. I think
             | even to experienced async Rust programmers, things like
             | Tokio mutexes being really hard to use correctly can be a
             | bit surprising.
             | 
             | Also, as another comment on the thread points out [1],
             | languages where futures are active by default can have the
             | opposite problem.
             | 
             | [1] https://news.ycombinator.com/item?id=45467188
        
       | foota wrote:
       | https://sunshowers.io/posts/cancelling-async-rust/#the-pain-...
       | was the most interesting part of this for me, as I can totally
       | see making mistakes like this.
        
         | schmichael wrote:
         | I'm a Go developer and this was still useful for me! Obviously
         | Rust devs are more accustomed to more assistance from their
         | tools than Go devs, but just about every gotcha listed is
         | something that can happen in Go with goroutines, channels,
         | select, and other shared concurrency primitives.
        
           | jcgrillo wrote:
           | !m schmichael
        
       | nijaar wrote:
       | for a sec i thought DEI was going too far
       | 
       | is the title like that on purpose?
        
       | nofriend wrote:
       | doesn't rust have raii?
        
         | sunshowers wrote:
         | It does, but you can only run synchronous code on drop. This is
         | what "async drop" is supposed to handle -- things like issuing
         | ROLLBACK statements to the database on cancellation.
         | 
         | It also wouldn't help when you have no valid state to restore
         | to, as in the mutex example in the post.
        
           | 0x1ceb00da wrote:
           | tokio-postgres handles this by just dispatching the
           | "ROLLBACK" command in impl Drop and ignoring the response.
           | https://github.com/rust-postgres/rust-
           | postgres/blob/a7a49a90...
           | 
           | Is this not enough? What could go wrong? If the network
           | connection dies or the task is cancelled, I'm assuming the
           | database server cleans up the connection state and does a
           | rollback automatically.
           | 
           | And adding async Drop will probably add a whole new set of
           | footguns.
        
             | _davide_ wrote:
             | > What could go wrong?
             | 
             | LoL, an insane amount of things. TCP connections are an
             | illusion of safely, for the purpose of database commits use
             | UDP packets as a model instead, it'll be much closer to
             | reality.
        
               | 0x1ceb00da wrote:
               | > an insane amount of things
               | 
               | List a couple
               | 
               | > TCP connections are an illusion of safely
               | 
               | Why?
        
       | tison wrote:
       | Rust's Future is somehow like move semantics in C++, where you
       | may leave a Future in an invalid state after it finishes.
       | Besides, Rust adopts a stackless coroutine design, so you need to
       | maintain the state in your struct if you would like to implement
       | a poll-based async structure manually.
       | 
       | These are all common traps. And now cancellations in async Rust
       | are a new complement to state management in async Rust (Futures).
       | 
       | When I'm developing the mea (Make Easy Async) [1] library, I
       | document the cancel safety attribute when it's non-trivial.
       | 
       | Additionally, I recall [2] an instance where a thoughtless async
       | cancellation can disrupt the IO stack.
       | 
       | [1] https://github.com/fast/mea
       | 
       | [2]
       | https://www.reddit.com/r/rust/comments/1gfi5r1/comment/luido...
        
       | beanjuiceII wrote:
       | i am honestly glad i don't write rust anymore
        
       | dxxvi wrote:
       | My Rust knowledge is too low to understand. However, thank you
       | very much for the article. I hope AI will learn it and explain to
       | me in a few years :-)
        
       ___________________________________________________________________
       (page generated 2025-10-04 23:01 UTC)