3 ms·
Rust doesn’t model IO in terms of types though, does it? Sure, an async function likely does IO, but maybe it’s just receiving from a channel? At the same time
by cube2222 2y ago
Rust doesn’t model IO in terms of types though, does it? Sure, an async function likely does IO, but maybe it’s just receiving from a channel? At the same time 90% (completely made up number) of Rust just does plain blocking IO and you absolutely won’t get that in the signature either (other than looking into error types, that is). We’re not in Haskell here. You can just make a blocking call in your async code, not notice it does IO (or you’re just not proficient in async), and it falls to pieces, your runtime just hung.
While “you can just use pollster” means you now have to wrap every function call to an async-first library in your async-less codebase. Again, this isn’t “composing well” in my book, it absolutely does create a divide in the ecosystem.
While in Go all code is written in a blocking fashion, but all IO is done async (either via threadpool or non-blocking syscalls), handled by the runtime, because the threads are green. Of course, that’s less performant and falls apart if you start using C libraries that do blocking IO (but it’s not like Rust would do better in that latter case).
At the same time, whether modeling IO in the type system is good or not, is I think something where opinions very much differ.
To be clear, again, I’m not bashing Rust async in general, I just think it’s a tradeoff and I very much think its usability is worse than just writing straightforward blocking code.
- dgroshev 2y agoI disagree with your "just" in "just receiving from a channel". Receiving from a channel is hardly distinguishable from IO, since it can take an arbitrary amount of time and the receiver needs to handle external resource failures (ie the other side of the channel getting dropped). That's very different from normal sync functions parsing JSON, adding matrices, or doing date math. Also note how functions that can't block are still sync, like tokio::sync::oneshot::Sender::send. I also can't agree with the argument that if it's possible to hang the runtime there's no point in explicit async. Yes people can still make mistakes, but it's better when it's explicitly a mistake and not just haphazard IO everywhere being the norm. What is the "async-less" codebase for? Why does it have to be "async-less"? A Go codebase is fully async at all times, so why shouldn't a Rust system extend async up to main() (or at least encapsulate IO-heavy parts in a separate async part)? Runtime behaviour of async rust is ~equivalent to Go, so coming back to my original point, the difference is that 1) Rust allows to have non-async, predictable functions, async is opt-in 2) the opt-in must be explicit 3) you have more choice between intra- and inter-future concurrency (select! vs spawn). All those points are important and empowering, they aren't problems to be solved. I agree that the flip side is that Rust forces explicit decisions upfront, but that's one of the core premises of Rust.