Live data from Hacker News

Why asynchronous Rust doesn't work

theta.eu.org

71–80 of 499 posts

Re: Why asynchronous Rust doesn't work

#71
None of this was about asynchronous rust and all of it was complaining that Closures are hard.

Closures are hard, but a lot of it's for good reason. As with all of the hard things Rust does, it's because it's trying to be correct.

Re: Why asynchronous Rust doesn't work

#72
post #15

I'm surprised Rust went with a Scala-inspired async/await considering it wasn't well-liked outside of PLT enthusiasts. JavaScript adopted it as I don't think you could bolt any other type of sugar on top of the event loop in browsers... but Rust shouldn't suffer from such baggage.

C# and C++ adopted basically the same model.

Note that C++ offers async as a library and not as a programming language feature which Bjarne Stroustrup is strongly against. He talk more about why in "The Design and Evolution of C++" but i don't have the material at hand. As far as i remember, his main argument was that there is no concurrency model to fit them all and thus it doesn't worth to add new syntax and semantics for a specific model except to make the PL more complex and harder to implement.

Re: Why asynchronous Rust doesn't work

#73
post #30

I've been primarily coding in rust since 2018. I never cared for async/await, and I've never used it. (at some point, coding event loops became very natural/comfortable for me, and I have no trouble writing "manual" epoll code with mio/mio_httpc). one nice thing about rust's async/await is, you don't have to use it, and if you don't, you don't pay for it in any way. sure, I run into crates that expect me to bring in…

Mmmyeah, they said this about python too...

Re: Why asynchronous Rust doesn't work

#74
post #15

Earlier quoted context omitted.

C# and C++ adopted basically the same model.

Note that C++ offers async as a library and not as a programming language feature which Bjarne Stroustrup is strongly against. He talk more about why in "The Design and Evolution of C++" but i don't have the material at hand. As far as i remember, his main argument was that there is no concurrency model to fit them all and thus it doesn't worth to add new syntax and semantics for a specific model except to make the P…

The main piece is stackless coroutines which definitely aren't a library.

Re: Why asynchronous Rust doesn't work

#75

A bigger problem in my opinion is that Rust has chosen to follow the poll-based model (you can say that it was effectively designed around epoll), while the completion-based one (e.g. io-uring and IOCP) with high probability will be the way of doing async in future (especially in the light of Spectre and Meltdown). Instead of carefully weighing advantages and disadvantages of both models, the decision was effectively…

> Instead of carefully weighing advantages and disadvantages of both models, the decision was effectively made on the ground of "we want to ship async support as soon as possible" [1].

That is not an accurate summary of that comment. withoutboats may have been complaining about someone trying to revisit the decision made in 2015-2016, but as the comment itself points out, there were good reasons for that decision.

Mainly two reasons, as far as I know.

First, Rust prefers unique ownership and acyclic data structures. You can make cyclic structures work if you use RefCell and Rc and Weak, but you're giving up the static guarantees that the borrow checker gives you in favor of a bunch of dynamic checks for 'is this in use' and 'is this still alive', which are easy to get wrong. But a completion-based model essentially requires a cyclic data structure: a parent future creates a child future (and can then cancel it), which then calls back to the parent future when it's complete. You might be able to minimize cyclicity by having the child own the parent and treating cancellation as a special case, but then you lose uniqueness if one parent has multiple children.

(Actually, even the polling model has a bit of cyclicity with Wakers, but it's kept to an absolute minimum.)

Second, a completion-based model makes it hard to avoid giving each future its own dynamic allocation, whereas Rust likes to minimize dynamic allocations. (It also requires indirect calls, which is a micro-inefficiency, although I'm not convinced that matters very much; current Rust futures have some significant micro-inefficiencies of their own.) The 2016 blog post linked in the comment goes into more detail about this.

As you might guess, I find those reasons compelling, and I think a polling-based model would still be the right choice even if Rust's async model was being redesigned from scratch today. Edit: Though to be fair, the YouTube video linked from withoutboats' comment does mention that mio decided on polling simply because that's what worked best on Linux at the time (pre-io_uring), and that had some influence on how Futures ended up. But only some.

…That said, I do agree Pin was rushed and has serious problems.

Re: Why asynchronous Rust doesn't work

#76
post #26

I don't think any of this is actually a reason why async rust doesn't work. The function color problem is a bit overblown, it hasn't broken nodejs yet either. Most code is sync, sync can call async and vice versa, and the ecosystem hasn't come tumbling down yet (since most Rust isn't whichever async application runtime for web apps is currently in vogue - its a systems language after all). Either way there are bigger…

I really don't see a problem with function color in a typed language. Every function has color and compiler enforces you pass right color. Async is just another type that may have some convenient syntax. It might be inconvenient in untyped languages like js since you couldn't compose them.

Re: Why asynchronous Rust doesn't work

#77
post #18

For a few months while I was between jobs, I decided to learn Rust. Because async code is supposed to be better than threaded code, I spent all my time trying to write async Rust. Big mistake! I never accomplished what I set out to accomplish. Most of my experience is in C#, where the difference between async and threaded code is mostly syntax sugar. (Until you get into the details.) (And async code makes writing UI…

You had chosen the really hard way to learn Rust. The most of problems for a beginner comes from lifetimes and borrowing. Async code by itself is harder topic when it comes to lifetimes and borrowing. When you use object in a concurrent manner, then you need to have two or more references to it, who would own object? When object has his owner, object lives. When object lost his owner, object dropped. Someone must own it. And borrow-checker need to have a way to prove, that owner will live longer than every one of your threads using this object, otherwise borrow-checker would start to complain and refuse to compile your code. If you are trying to get mutable access to object how you are going to ensure that it would be done in a safe manner without data races? How are you going to explain borrow-checker that it is a safe way?

I'm trying to say, that mostly you problems was lifetimes and borrowing, like with other beginners, but problems were magnified to a level when beginner have no chances to make it work.

Re: Why asynchronous Rust doesn't work

#78

I'm surprised Rust went with a Scala-inspired async/await considering it wasn't well-liked outside of PLT enthusiasts. JavaScript adopted it as I don't think you could bolt any other type of sugar on top of the event loop in browsers... but Rust shouldn't suffer from such baggage.

I'm confused... Scala doesn't have async/await.

Re: Why asynchronous Rust doesn't work

#79
post #17

This article is a bit confusing. The concrete issues described all relate to closures, but the author says the real problem is async and they liked Rust before that (when it had the closures they have concrete issues with?).

He says the problem is the proliferation of async libraries, which forces their use and pollute everything they touch in association with the closure problem. It's the combination of both.

He doesn't give any concrete examples of that though.

Re: Why asynchronous Rust doesn't work

#80

A bigger problem in my opinion is that Rust has chosen to follow the poll-based model (you can say that it was effectively designed around epoll), while the completion-based one (e.g. io-uring and IOCP) with high probability will be the way of doing async in future (especially in the light of Spectre and Meltdown). Instead of carefully weighing advantages and disadvantages of both models, the decision was effectively…

I agree that Rust async is currently in a somewhat awkward state.

Don't get me wrong, it's usable and many projects use it to great effect.

But there are a few important features like async trait methods (blocked by HKT), async closures, async drop, and (potentially) existential types, that seem to linger. The unresolved problems around Pin are the most worrying aspect.

The ecosystem is somewhat fractured, partially due to a lack of commonly agreed abstractions, partially due to language limitations.

There also sadly seems to be a lack of leadership and drive to push things forward.

I'm ambivalent about the rushing aspect. Yes, async was pushed out the door. Partially due to heavy pressure from Google/Fuchsia and a large part of the userbase eagerly .awaiting stabilization.

Without stabilizing when they did, we very well might still not have async on stable for years to come. At some point you have to ship, and the benefits for the ecosystem can not be denied. It remains to be seen if the design is boxed into a suboptimal corner; I'm cautiously optimistic.

But what I disagree with is that polling was a mistake. It is what distinguishes Rusts implementation, and provides significant benefits. A completion model would require a heavier, standardized runtime and associated inefficiencies like extra allocations and indirection, and prevent efficiencies that emerge with polling. Being able to just locally poll futures without handing them off to a runtime, or cheaply dropping them, are big benefits.

Completion is the right choice for languages with a heavy runtime. But I don't see how having the Rust dictate completion would make io_uring wrapping more efficient than implementing the same patterns in libraries.

UX and convenience is a different topic. Rust async will never be as easy to use as Go, or async in languages like Javascript/C#. To me the whole point of Rust is providing as high-level, safe abstractions as possible, without constraining the ability to achieve maximum efficiency . (how well that goal is achieved, or hindered by certain design patterns that are more or less dictated by the language design is debatable, though)

Post reply on HN