[HN Gopher] Six ways to make async Rust easier
___________________________________________________________________
Six ways to make async Rust easier
Author : gbrown_
Score : 50 points
Date : 2021-06-17 18:03 UTC (4 hours ago)
(HTM) web link (carllerche.com)
(TXT) w3m dump (carllerche.com)
| akiselev wrote:
| That comic at the end perfectly sums up my reaction (mostly
| because I want GATs though :)).
|
| I'm not fully sold on Rust's existing async/await implementation
| but how would this suggestion even be implemented? It's a massive
| breaking change on multiple fronts and I don't know how a new
| edition would paper over the impedance mismatch between the new
| system and async libraries written in the older edition. Feels
| like I need another year or two of using async rust in production
| to even begin to form an opinion on this.
|
| How would this work on embedded? I haven't done embedded since
| long before I could use async/await or Rust but it sounds like
| implicit _anything_ (except maybe autoref /deref) is a deal
| breaker.
| carllerche wrote:
| I believe it would be possible to implement using an edition in
| a way that any *old* code is compatible with new code.
|
| Regarding implicitness, a lot in Rust already is implicit (e.g.
| type inference). Good implicitness only feels weird at first,
| before we get used to it. I linked to Aaron Turon's article on
| reasoning footprint, but as a follow up, there is withoutboat's
| article on "not explicit" that goes into the topic more:
| https://boats.gitlab.io/blog/post/2017-12-27-things-explicit...
| staticassertion wrote:
| It feels like the fundamental issue here is shared mutable state,
| and much of the solution is to just not have that.
|
| I don't believe this requires a change to async-await though. It
| just means you want to wrap up your async-await in its own
| synchronization primitive, or just avoid the need for it.
|
| This is easy to do with a channel system, or an actor system,
| where the queue is the synchronizing primitive. An actor's
| message handler can internally be asynchronous, so long as it
| processes messages one at a time (but it can yield during
| processing to another actor).
|
| We can get actors in rust pretty trivially imo. It's just a task
| with state and a message interface.
| layoutIfNeeded wrote:
| These personas (i.e. "Alan") are annoying enough when used by
| business people, but in the context of a programming language
| it's beyond cringe.
| littlestymaar wrote:
| > I believe that the better reason for asynchronous is it enables
| modeling complex flow control efficiently. For example, patterns
| like pausing or canceling an in-flight operation are challenging
| without asynchronous programming.
|
| This is exactly why I think _async /await_ is a must have for any
| language aimed at back-end. Nodejs has a lot of pitfalls (promise
| cancellation being a major one), but moving from it to Go felt
| like huge step back to me: having to use channels to synchronize
| your tasks feels really cumbersome when you're used to
| promises/Future with combinators and _async /await_.
| convolvatron wrote:
| I find continuations alot easier to reason about as a
| programmer. I think async/await was chosen here because in
| simple cases it pisses off the borrower less
| cube2222 wrote:
| I obviously can't see your code, but in my experience, working
| with Go daily for years, explicit channels are an extremely
| rare occurrence.
|
| If you want to run multiple tasks and wait for them to finish,
| you use a WaitGroup. If you want to stop processing on first
| error encountered, you use an ErrGroup. And cancellation with
| contexts also works very well overall.
|
| I actually think Go does it way better than all the "visible
| async/await" languages. Because usually when you write code,
| you just want to await the result, so in Go, awaiting is the
| default. In order to run a task without awaiting immediately,
| you use the 'go' keyword (at least that's the semantics of it
| all). It also omits the function coloring problem (this is a
| very big advantage), because all functions are async.
|
| For my use cases channels are usually only useful in two
| situations:
|
| 1. Asychronous producers and consumers.
|
| 2. Creating an "actor" which receives messages on a channel/s
| and processes them in a serialized manner. Where the messages
| come from multiple event sources.
|
| As an aside, I do understand why performance oriented languages
| like Rust choose the async/await path, as async by default
| carries a performance penalty / requires a runtime. I don't
| however accept it in non-performance oriented languages.
| akiselev wrote:
| _> As an aside, I do understand why performance oriented
| languages like Rust choose the async /await path, as async by
| default carries a performance penalty / requires a runtime. I
| don't however accept it in non-performance oriented
| languages._
|
| Ironically, I think Rust futures could be expanded into Go-
| like uncolored functions relatively easily without even much
| of a runtime. Since they're stack allocated by default,
| driving a future to completion is a matter of calling
| Future::poll in a while loop and handling the waker callback.
| I feel like the runtimes are there mostly to provide a
| unified interface to kernel APIs like io_uring/epoll/etc and
| ease ownership/task allocation.
| Animats wrote:
| Go really does do this better. Go is a green thread system.
|
| In Rust, if you're actually doing any compute work, you're
| stalling out the async system. In Go, if you compute for a
| while, the scheduler will let someone else run. You can get
| all the CPUs working. This is well matched to writing web
| back ends.
|
| Rust's "Async" seems to be designed for a very specific use
| case - a program running a very large number of network
| connections, most of which are waiting. If you're doing
| something where you need all available CPUs to get the work
| done, it's a bad fit.
| anderskaseorg wrote:
| Rust has always had threads (https://doc.rust-
| lang.org/book/ch16-01-threads.html), along with incredibly
| powerful libraries like Rayon (https://github.com/rayon-
| rs/rayon) to take advantage of them. You can even use
| threads with async, if you want, using a multi-threaded
| scheduler like Tokio with the rt-multi-thread feature flag
| (https://docs.rs/tokio/1.7.0/tokio/runtime/index.html#multi
| -t...).
___________________________________________________________________
(page generated 2021-06-17 23:00 UTC)