5 ms·
I wish Rust would just make everything (that can be async) in the stdlib async, but also provide a simple primitive for "block the current thread on this async
by lalaithion 5y ago
I wish Rust would just make everything (that can be async) in the stdlib async, but also provide a simple primitive for "block the current thread on this async call", and guarantee that all of the stdlib functions that are async work when called with that primitive. Something like "block!(async_fn())" or "async_fn().block". Or even just allow non-async functions to .await, with the meaning of "block the world until this finishes running"
What am I missing that makes this undesirable?
- ibraheemdev 5y agoThat would mean a runtime in the standard library, which was explicitly rejected. Different runtimes have different pros and cons and different use cases, choosing one and putting it in the standard library means that all others are second class and probably not well supported. For example, some might want an io-uring based async runtime, while others want a runtime-per-core system for a web server, and another might want a simple lightweight runtime for an embedded environment. Someone else wants to use raw OS primitives without ever spawning a runtime (to use block_on and an async function that does IO, you still need a runtime running in the background). A better solution is to provide standard interfaces for runtimes and libraries to develop against, something that is being worked on.
- steveklabnik 5y agoThere are a couple of different objections to this, though there is also a lot of support. The current state of it is "someone needs to write up an RFC." If you want the full gory details: https://github.com/rust-lang/rust/pull/65875 https://github.com/rust-lang/rust/pull/65875
- ibraheemdev 5y agoWell that's just the "simple primitive for 'block the current thread on this async call'", which can, with the new `Wake` trait, be written in around 10 lines of code. The real change would be adding an actual runtime to the standard library.
- steveklabnik 5y agoPotato, potato. Both involve adding some form of runtime, now we’re just negotiating details. (I personally would like a very very simple one like the ten LOC version, but wouldn’t want anything more.)
- ibraheemdev 5y agoWell the block_on runtime is only meant for testing or bridging sync and async code, so it wouldn't solve the problem :)
- steveklabnik 5y agoIt solves my problem, which is why I like it as a solution (documentation, examples, and tests), haha!
- dcow 5y agoBecause then you mess up all the other async tasks that would be spawned by the callee and possibly deadlock your program (without an async runtime in the stdlib). If you're using `async_std`, right now you simply, explicitly, introduce the runtime: let fut = async_fn(); let res = async_std::task::block_on(fut)?; (pulling out the Future into fut is just illustrative, it can of course be a single line) instead of: let res = async_fn().await?; So it's already pretty darn easy to do what you want. Also, here's an easy way to make it feel like a keyword: use std::result::Result; use std::error::Error; use async_std::task::block_on as block; fn main() -> Result<(), Box<dyn Error>> { let fut = async_std::fs::read_to_string("./Cargo.toml"); let file = block(fut)?; println!("{}", file); Ok(()) } --- [package] name = "async-sandbox" version = "0.1.0" edition = "2018" [dependencies] async-std = "1.10" One final thing to point out here is that, unlike some other languages, in Rust you can call any async function just like any normal function. The only thing that requires a runtime is resolving the returned future. So it's not so much that functions are colored but that resolving futures needs a runtime and there is none in the current standard lib.
- ibraheemdev 5y agoI think the OP meant that those who did not want async would use block_on, while the async API would just work for those who wanted to use it.
- dcow 5y agoI tried to edit for clarity. The issue is the "bringing in the runtime", or the `async_std` part as you point out elsewhere. I guess I don't see a huge benefit in making a keyword like `block` that aliases `async_std::task::block_on`. Indeed you could just `use async_std::task::block_on as bock;` and be on your way.
- Matthias247 5y agoFirst and foremost that Rust is a systems programming language that shouldn't have any kind of implicit runtime. If you run any implicit eventloop, that property would no longer be given. Next, it wouldn't even be desirable for lots of applications. Async IO and functions are not necessarily be faster than synchronous operations - it might as well the opposite if you don't have a lot of concurrency. E.g. if you read from one blocking socket, you do a single `read()` syscall. Add async IO, and you need an additional `select/epoll_wait` call. Then there is an actual cost for composing Futures which are large values on the stack, sometimes having to box them (since otherwise recursion won't work and dynamic dispatch will neither, etc). The latter might certainly be avoidable with a different kind of "async design" than what Rust currently has, but there will always be some tradeoffs.
- pjmlp 5y agoC and C++ happen to have a runtime, or whatever you feel like calling the code that calls main(), runs functions registered on executable load, registered atexit(), soft floating point emulation, running constructors/destructors on start/exit, handling exceptions, threading support.
- deleted 5y ago[deleted]
- scoutt 5y agoBut it's not implicit and/or required. In embedded you can use C/C++ without a runtime. > calling the code that calls main(), runs functions registered on executable load, registered atexit(), soft floating point emulation, running constructors/destructors on start/exit, handling exceptions, threading support. You call main() (or whatever) from your reset handler, there is no "executable load", there is no "atexit()", floating point can be hard-instructions (if available), call __libc_init_array() to call constructors if required, you usually don't use exception handling, and of course there is no threading support. Perhaps you can call "runtime" the instructions inserted to call constructors/destructors but I think it doesn't qualify.
- vp8989 5y agoWouldn't you be using 2 threads for at least some of the duration of every instance of that? Seems like it would scale very poorly.
- pjmlp 5y agoThis is the approach Microsoft took with WinRT, and Google with Android, to force devs to go fully async. It did not work that well, as not everyone is confortable in an async only world.