5 ms·
> After two decades of JavaScript and decent experience with Go, this is the most significant source of frustration and friction with Rust. It’s not an insurmou
by dilippkumar 3y ago
> After two decades of JavaScript and decent experience with Go, this is the most significant source of frustration and friction with Rust. It’s not an insurmountable problem, but you must always be ready to deal with the async monster when it rears its head. In other languages, async is almost invisible.
I am a former C and C++ programmer who lived calling into pthread almost every week for a decade. I use async rust everywhere now.
I don’t get the hate that async gets. In my opinion, everyone should be using async for everything. Including stuff that’s seemingly single threaded “simple” stuff.
- ilaksh 3y agoThe problem that I am running into at the moment is that a few things like the rhai Engine aren't Send and I am trying to use them in an async closure. What GPT-4 suggested was creating a tokio Runtime inside the thread and then block_on(). I will try it tomorrow. (This is the first significant Rust project for me.)
- codys 3y agoThe answer here is almost certainly one of these: 1. Fix rhai::Engine so it is Send (if !Send is unintentional) 2. Use tokio::spawn_blocking or normal threads to run the rhai::Engine bits 3. Don't hold the rhai::Engine across an await point. Which one depends on rhai Engine details and what you want to accomplish. Doing a block on inside a new thread seems unlikely to do anything useful (unless there's some undisclosed detail that makes it reasonable). I encourage you to ask about this in the rhai repo in a discussion or issue.
- ilaksh 3y ago1. Not applicable unless it is absolutely required, and not something I will consider until I have exhausted other options, since there is a reason they have not made it Send already. It will likely be quite complex and enlarge the scope. 2. I am using a normal thread but when I make it async the compiler wants Send. 3. The await point is in code that uses the engine. I am not sure there is another good option, since I need to use an API that has several libraries all of which are async. The block_on is to allow the tokio Runtime created in that thread to execute/poll it So, thank you for your input, I will test out the suggestion that I mentioned above, and then maybe look into spawn_blocking if that doesn't work.
- pornel 3y agoThat ChatGPT suggestion is dodgy. Tokio is going to complain when you create a runtime while in async runtime, and refuse block_on in an async context. You can have multiple runtimes, but create them ahead of time, in synchronous main, and keep a Handle to them. But you probably need: https://docs.rs/tokio/latest/tokio/task/struct.LocalSet.html https://docs.rs/tokio/latest/tokio/task/struct.LocalSet.html
- FridgeSeal 3y ago> 2. I am using a normal thread but when I make it async the compiler wants Send. This is likely because you are using the multi-threaded scheduler, which requires futures to be Send, even if you’re running only a single thread. This is because Tokio is based on a “work stealing” runtime, so in “normal” operations, expects the futures themselves to be able to be shuffled around threads where necessary. For you use case, try running the single threaded executor, additionally try the “local set” executors. These do not require Send as they are statically guaranteed to be confined to the thread they are spawned on. Block on will also work. Out of curiosity though, how is the interaction with Rhai performed, are you passing around the engine, and executing the code at certain points where applicable? Do you just need certain results from it sometimes? Etc?
- ilaksh 3y agoRight, GPT-4 explained that much about shuffling. I am trying to do all the interaction with MPSC channels but was still running into issues. I think I have a lot of good advice to go try now. Thanks.
- cchance 3y agoIt being !Send means its not safe to be sent lol, that's down to the way they implemented rhai engine not rust. It's just that rust catches that it's not safe to send because of the trait bounds.
- brochington 3y agoFrom the rhai Engine docs: `Currently, Engine is neither Send nor Sync. Use the sync feature to make it Send + Sync.`
- jgilias 3y agoYou might be able to use the LocalSet (https://docs.rs/tokio/latest/tokio/task/struct.LocalSet.html https://docs.rs/tokio/latest/tokio/task/struct.LocalSet.html) to run !Send futures on a single thread.
- Measter 3y agoAccording to the documentation[0], enabling the "sync" feature should make the Engine Send+Sync. From a quick look at the source, it looks like it switches between using Rc/RefCells and Arc/RwLock. [0] https://docs.rs/rhai/1.16.2/rhai/#enable-special-functionalities https://docs.rs/rhai/1.16.2/rhai/#enable-special-functionali...
- badrequest 3y agoRecently tried writing some async Rust to compare the error handling when nested async calls are made to how errors are handled in Go, and it seemed like the trivial example I was trying to write up simply couldn't be done without involving Tokio. That barrier simply doesn't exist in Go, or C#, or Typescript. For instance, you apparently cannot `await` in the main function without a decorator you import from, you guessed it: Tokio.
- lmm 3y agoYou need an async runtime to run async code yes, and Rust's isn't built in. Why does that matter though? Rust has a decent package manager; add the dependency and move on.
- cchance 3y agoWhy are you trying to avoid Tokio lol, tokio is the defacto async runtime in rust, saying you're trying to avoid it is like saying you're trying to avoid async while writing async, somehow people act like if they merged tokio into std and instead of #[tokio::main] or whatever you had to do #[async::main] it would somehow be better. Once you stop fighting the fact that tokio = async rust for 99% of cases, things are quite smooth.
- galangalalgol 3y agoTokio simply doesn't meet the ise case of some people doing wasm, and many people doing embedded. That isn't a huge deal, just don't use it right? Except many otherwise usable crates seem to adopt tokio unnecessarily.
- cmrdporcupine 3y agoYour "lol" is entirely inappropriate.
- yencabulator 3y agoIf Tokio was the final answer, https://github.com/tokio-rs/tokio-uring/ https://github.com/tokio-rs/tokio-uring/ wouldn't exist.
- deleted 3y ago[deleted]
- ridiculous_fish 3y agoCan you please elaborate on this? What's an example of a seemingly single threaded simple task where you would turn to async? Does "use async rust everywhere now" imply "use tokio everywhere now?" Honest question.
- pornel 3y agoThe way closures made it easy to encapsulate code and state in an object you can run at any time, async encapsulates code and state for ability to run and pause or cancel at any time. This is handy for I/O that can be interleaved and cancelled. You can (ab)use it for other things like generators or various DIY multitasking operations. It can also be a state machine generator (e.g. AI of actors in a game). But I think OP just meant async for typical networking and DB interfaces. And yes, this usually implies the Tokio dependency.
- galangalalgol 3y agoI think it is the infectiousness of it. Especially in embedded or wasm contexts, the predominant async may not be the async you want. Wasm being the author's use case would definitely have provided a different perspective. Similarly, I find tasks that use or reuse large buffers to avoid the performance hits from allocation, often benefit from old fashioned thread pools. Bump or shard allocators can make this work ok, but in the cases where you are cpu bound on tight loops of vectorizable operations, thread pools perform better. Async is a good tool, but there are contexts it isn't optimal for.
- deleted 3y ago[deleted]
- Alifatisk 3y ago> In my opinion, everyone should be using async for everything. Including stuff that’s seemingly single threaded “simple” stuff. I learned the opposite, that programmers tend to make everything unnecessarly async when it's not needed causing more complexity and mental load.