3 ms·
> By doing so, one would set up a multi-threaded runtime which mandates that types are Send and 'static and makes it necessary to use synchronization primitives
by the_mitsuhiko 3y ago
> By doing so, one would set up a multi-threaded runtime which mandates that types are Send and 'static and makes it necessary to use synchronization primitives such as Arc and Mutex for all but the most trivial applications.
That is a very weird argument to make. Tokio has very convenient APIs (LocalSet + spawn_local) for spawning non Send futures that you temporarily await (which really is the only useful thing non Send futures can do).
If anything tokio significantly improved the user experience of async in Rust in general because it promoted Send futures.
- mre 3y agoThere's always a trade-off. By promoting Send futures, Tokio prioritizes safety and parallelism. However, this does add complexity for developers, especially newcomers. They need to be aware of the Send and 'static requirements and might have to use synchronization primitives more often. Because of this, I think promoting Send futures as the default is the wrong way to go. LocalSet + spawn_local are great, and I wish more developers would know about them, but the Tokio tutorial [1] doesn't mention that and focuses on the multi-threaded runtime instead. AFAIK LocalSet is only mentioned in the docs [2] [1]: https://tokio.rs/tokio/tutorial https://tokio.rs/tokio/tutorial [2]: https://docs.rs/tokio/latest/tokio/task/struct.LocalSet.html https://docs.rs/tokio/latest/tokio/task/struct.LocalSet.html
- zozbot234 3y ago> LocalSet + spawn_local are great, and I wish more developers would know about them, but the Tokio tutorial doesn't mention that and focuses on the multi-threaded runtime instead. Patches welcome: https://github.com/tokio-rs/website/ https://github.com/tokio-rs/website/
- the_mitsuhiko 3y agoAs someone who programs async Rust since early days (where tokio did not enforce Send bounds), people build themselves into horrible patterns (myself included). Once you go deep on non sendable futures, you can quickly end up creating something you shouldn't have. So I think it's more than sensible to tell people to do the right thing and then follow up on the exceptional case via API docs or a followup guide.
- mre 3y agoIndeed, while the Send bound can safeguard against potential concurrency issues, it also dictates a specific architectural direction for applications. Consider a web server: with the Send bound, you might be encouraged to design it such that each incoming request is handled by potentially any thread in a thread pool. Without that bound, you might lean towards a more lightweight, single-threaded model similar to Node.js, which doesn't require Send bounds and still excels at handling I/O-bound tasks. "Doing the right thing" can vary based on the context. For instance, in embedded systems where threads aren't available, requiring futures to be Send is unnecessary. Thankfully, the standard library does not enforce this and neither does Tokio with spawn_local, but embassy exists because there's a genuine need for async frameworks tailored to the unique constraints and requirements of embedded systems.
- thinkharderdev 3y agoWhere it has tripped me up in the past it has been because of the way the compiler rewrites async code and you implicitly capture scope at await points. So you code doesn't compile because "future is not Send" and it's not immediately obvious why. And then it turns out that 100 lines previously you had a parking_lot::Mutex local variable which got captured in scope. So you need to refactor a bit to make sure that local variable is out of scope at your await point.