Earlier quoted context omitted.
Yes, you can encode state machines manually, but it will be FAR less ergonomic than the async syntax. Rust has started with a library-based approach, but it was... not great. Async code was littered with and_then methods and it was really close to the infamous JS callback hell. The ergonomic improvements which async/await brings is essentially a raison d'être for incorporating this functionality into the language.
Interesting! For comparison, Haskell went with the library approach but has the syntactic sugar of the equivalent of `and_then` built into the language. (I am talking about Monads and do-notation.) It's a bit like iterating in Python: for-loops are a convenient syntactic sugar to something that can be provided by a library.
Why asynchronous Rust doesn't work
101–110 of 499 posts
Re: Why asynchronous Rust doesn't work
#102Async isn't really the problem - the same issue pops up with error handling, with resource management, with anything where you want to pass functions around. The real problem is that Rust's ownership semantics and limited abstractions mean it doesn't really have first-class functions: there are three different function types and the language lacks the power to abstract over them, so you can't generally take an expres…
Nit, you can already abstract over lifetimes. But I really agree with the HKT comment, in principle. In practice it's not so bad. Abstracting over async, mutability, and borrows would be more important to smooth over a few of these issues (there are ways around both, but it's not always obvious). Most of these issues arise for library authors. I think a lot of folks in the ecosystem today are writing libraries instea…
My point is that absent some horrible specific hacks, HKT is a necessary part of doing that. A type that abstracts over Fn, FnMut, or FnOnce is a higher-kinded type.
Re: Why asynchronous Rust doesn't work
#103Earlier quoted context omitted.
> So... don’t use first-class functions so much? It’s a systems language, not a functional language for describing algorithms in CS whitepapers. Then maybe it was a mistake to adopt an async paradigm from functional languages that relies heavily on the idea that first-class functions are cheap and easy? (FWIW I think Rust was right to pick Scala-style async; it's really the only nice way of working with async that I'…
It's always possible there's a better way. If you know of one, maybe you can write a proposal? There is always Rust v2. Rust lacks a BDFL and so it's almost like the language grows itself. Chances are, the async model that was used was picked because it was arrived upon via consensus.
Re: Why asynchronous Rust doesn't work
#104Re: Why asynchronous Rust doesn't work
#105Earlier quoted context omitted.
> anything where you want to pass functions around. You should read the article! The author goes into this in fascinating detail.
I did read the article; it's overly focused on async (especially the headline). The body quite correctly analyses the problem with functions, but misses that this is much more general than async; just adopting a different async model isn't a solution.
The author could have made the topic about closures, but that’s not what they wanted to talk about.
Re: Why asynchronous Rust doesn't work
#106The big issue is that non-GC concurrency is still effectively a CS research topic. In a GC language, you can "just" let the GC system clean up after you're done--who owns allocation and cleanup isn't an issue. In a non-GC language, who allocates and cleans up what and who pays for that suddenly becomes a fundamental part of the domain. For example, I have seen a zillion "lock-free" data structure libraries in Rust, a…
Re: Why asynchronous Rust doesn't work
#107Async isn't really the problem - the same issue pops up with error handling, with resource management, with anything where you want to pass functions around. The real problem is that Rust's ownership semantics and limited abstractions mean it doesn't really have first-class functions: there are three different function types and the language lacks the power to abstract over them, so you can't generally take an expres…
Rust absolutely does have first-class functions, though. Their type is `fn(T)->U`. The "three different function types" that you refer to are traits for closures. And note that closures with no state (lambdas) coerce into function types.
higher_order(is_zero);
higher_order(|n| n == 0);
fn higher_order(f: fn(i32)->bool) {
f(42);
}
fn is_zero(n: i32) -> bool {
n == 0
}
It's true that when you have a closure with state then Rust forces you to reason about the ownership of that state, but that's par for the course in Rust.Re: Why asynchronous Rust doesn't work
#108Earlier quoted context omitted.
Without introducing Rust 2? Highly unlikely. I should have worded my message more carefully. Completion-based model is not a silver bullet which would magically solve all problems (though I think it would help a bit with the async Drop problem). The problem is that Rust async was rushed without careful deliberation, which causes a number of problems without a clear solution in sight.
> The problem is that Rust async was rushed without careful deliberation As someone who observed the process, this couldn't be further from the truth. Just because one disagrees with the conclusion does not mean that the conclusion was made in haste or in ignorance. > Without introducing Rust 2? Highly unlikely. This is incorrect. async/await is a leaf node on the feature tree; it is supported by other language featu…
Believe me, I do understand the motivation behind the decision to push async stabilization in the developed form (at least I think I do). And I do not intend to argue in a bad faith. My point is that in my opinion the Rust team has chosen to get the mid-term boost of Rust popularity at the expense of the long-term Rust health.
Yes, you are correct that in theory it's possible to deprecate the current version of async. But as you note yourself, it's highly unlikely to happen since the current solution is "good enough".
Re: Why asynchronous Rust doesn't work
#109Earlier quoted context omitted.
Without introducing Rust 2? Highly unlikely. I should have worded my message more carefully. Completion-based model is not a silver bullet which would magically solve all problems (though I think it would help a bit with the async Drop problem). The problem is that Rust async was rushed without careful deliberation, which causes a number of problems without a clear solution in sight.
> The problem is that Rust async was rushed without careful deliberation As someone who observed the process, this couldn't be further from the truth. Just because one disagrees with the conclusion does not mean that the conclusion was made in haste or in ignorance. > Without introducing Rust 2? Highly unlikely. This is incorrect. async/await is a leaf node on the feature tree; it is supported by other language featu…
Re: Why asynchronous Rust doesn't work
#110Earlier quoted context omitted.
Since you clearly have expertise, I'm curious if you might provide some insight into what would roughly be different in an async completion-based model & why that might be at a fundamental odds with the event-based one? Like is it an incompatibility with the runtime or does it change the actual semantics of async/await in a fundamental way to the point where you can't just swap out the runtime & reuse existing async…
I'm also curious about this. Boats wrote some about rust async and io-uring a while ago that's interesting[1], but also points out a very clear path forward that's not actually outside the framework of rust's Future or async implementation: using interfaces that treat the kernel as the owner of the buffers being read into/out of, and that seems in line with my expectations of what should work for this. But I haven't…
However, I wouldn't call this a problem with a polling-based model.
At least part of the goal here must be to avoid allocations and reference counting. If you don't care about that, then the design could have been to 'just' pass around atomically-reference-counted buffers everywhere, including as the buffer arguments to AsyncRead/AsyncWrite. That would avoid the need for AsyncBufRead to be separate from AsyncRead. It wouldn't prevent some unidiomaticness from existing – you still couldn't, say, have an async function do a read into a Vec, because a Vec is not reference counted – but if the entire async ecosystem used reference counted buffers, the ergonomics would be pretty decent.
But we do care about avoiding allocations and reference counting, resulting in this problem. However, that means a completion-based model wouldn't really help, because a completion-based model essentially requires allocations and reference counting for the futures themselves.
To me, the question is whether Rust could have avoided this with a different polling-based model. It definitely could have avoided it with a model where the allocations for async functions are always managed by the system, just like the stacks used for regular functions are. But that would lose the elegance of async fns being 'just' a wrapper over a state machine. Perhaps, though, Rust could also have avoided it with just some tweaks to how Pin works [1]… but I am not sure whether this is actually viable. If it is, then that might be one motivation for eventually replacing Pin with a different construct, albeit a weak motivation by itself.
[1] https://www.reddit.com/r/rust/comments/dtfgsw/iou_rust_bindi...