Welcome to the world of async
. Whatever intuitions you might have developed up to this point are better off neatly packaged, tied with a ribbon on top, then promptly thrown out of a window.
Any async functionasync fn(A) → B
is syntactic sugar for fn(A) → impl Future<Output = B>
. The “ownership” you are trying to carry over from the sync side doesn’t apply here.
Any F: impl Future
itself isn’t a singular memory location tagged with a few impl
's here and there. Rather: it’s a compiler-produced state machine that provides no guarantees whatsoever as to where or when or how, thread/execution/timing-wise, any next state snapshot is to be handled.
Meaning: the compiler has to assume the worst case scenario. As in: each and every await
point has to be considered a potential thread hand-off of the state A created by that await
point into the state B which follows next.
Since both a: Cell<i32>
and b: &Cell<i32>
in your scenario are (to be thought of as) initialized in a thread X and processed after the await
in the println!(...)
by a thread Y: you’ve got yourself a perfectly reasonable violation of the Sync
and Send
trait bounds.
In your particular example, it’s easy enough to solve. Simply hand out the ownership over a
itself instead of the its reference &b
. This compiles fine too. For the future reference (pun intended), when dealing with impl Future
in any shape or form, think less about the ownership over a singular memory location (sync) and more about the implicit synchronicity over the data captured within/by any particular await
point of the Future
itself. More often than not, it’ll help.
Should you happen to feel momentarily distraught by all the aforementioned, tough luck. Better yet: let me channel my inner Bethesda’s customer service from the times of Fallout 76:
Hello,
We are sorry you aren’t happy with the state of the
async
in the current edition of Rust. The memory ownership intuition you were meant to develop when working with single-threaded and/or parallel execution turned to be too expensive to port into our zero-cost concurrency framework, reinvented from scratch for the ultimate benefit to no one in particular.We aren’t planning to do anything about it.
Rust Async Support - International Department