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
asyncin 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