←back to thread

159 points mpweiher | 1 comments | | HN request time: 0.226s | source
Show context
anarki8 ◴[] No.43672225[source]
I find myself using channels in async Rust more than any other sync primitives. No more deadlock headaches. Easy to combine multiple channels in one state-keeping loop using combinators. And the dead goroutines problem described in the article doesn't exist in Rust.
replies(5): >>43672313 #>>43672959 #>>43674608 #>>43674871 #>>43675417 #
1. ninkendo ◴[] No.43672313[source]
Same. It’s a pattern I’m reaching for a lot, whenever I have multiple logical things that need to run concurrently. Generally:

- A struct that represents the mutable state I’m wrapping

- A start(self) method which moves self to a tokio task running a loop reading from an mpsc::Receiver<Command> channel, and returns a Handle object which is cloneable and contains the mpsc::Sender end

- The handle can be used to send commands/requests (including one shot channels for replies)

- When the last handle is dropped, the mpsc channel is dropped and the loop ends

It basically lets me think of each logical concurrent service as being like a tcp server that accepts requests. They can call each other by holding instances of the Handle type and awaiting calls (this can still deadlock if there’s a call cycle and the handling code isn’t put on a background task… in practice I’ve never made this mistake though)

Some day I’ll maybe start using an actor framework (like Axum/etc) which formalizes this a bit more, but for now just making these types manually is simple enough.