@angelthorns Basically the pattern I came up with is:
1) Create Vec that will store client mpsc channels (which are thankfully Clone) and messages you want to send them
2) Lock state structure (I used an RwLock because for the most common case, PRIVMSG and NOTICE, you don't need to mutate much client state)
3) Perform CRUD-style updates to the users
4) Enqueue messages to send to the users as well as their queues
5) Drop the lock
6) Process all the messages in the Vec you just created
It is... actually much more messy and heavy than the same C/C++ code, and not a super scalable design. But the architecture of a chat system makes it virtually unavoidable unless you store thousands of per-user locks... which is not great overhead either and requires locking/unlocking potentially thousands of locks per operation too.
I mean, hey, if anyone else has better ideas I'm open to it, but this seems to be the way everyone suggests doing it.
@angelthorns I guess an alternative could be to use Rust's stock mpsc queues which don't require .await but that is just as heavy weight and might block if the queue is full.
Also, a major footgun I found. Never use tokio's RwLock if you can avoid it. Sure, it's Send. But then you can cause a deadlock without even realising it, if you keep the lock held in a coroutine, then .await... if something else tries to use that same lock. BOOM. Deadlock.