To those who are interested in isolating async rust in a application you can do so by creating a creating a async runtime in a different thread from main. Then you can share data between the async runtime and your sync rust using ideally a channel or some global data. In Tokio the `#[tokio::main]` macro is just a wrapper for the `tokio::runtime::Builder` which when built calls `Runtime::block_on`. You can easily do all of this inside a thread instead of main and have your own isolated async runtime.
but how can i do work stealing on thousands of blocking tasks? i can see building a threadpool that handles a queue of tasks or something of that sort in sync, but how do you tell the threads to switch tasks when they are waiting for something
@@eddierandom6074 This is not the most widely applicable architecture you can use. If you want to do something fully integrated like what you mentioned you simply need a async runtime everywhere for that. The thing this architecture was originally suggested for was something like a discord clone where the sync side handles the user input and UI stuff while the async side handles the IO. I would definitely *not* suggest people consider this option unless they are making something like a single user application (and even then this isn't the ultimate solution a lot of the times). This is just one way to capture the async beast if you were curious on how to do so.
The development of async-std slowed down because the rust team is working on providing that functionality as part of std::async. So, hopefully at some point in the future, we will see (most of) the ecosystem defragmentation go away - they will all use std::async without depending on a specific runtime. The compiler team is working hard on implementing async traits as a language feature, which will allow the development of std::async to the standard of the rust std library.
I actually used to dislike async rust a lot. (Tokio is really overkill for most use cases imo, it also adds like 300 dependencies) But using async-executor etc. directly (basically the crates that smol is built ontop of) and futures-lite was a really nice experience (even tho some features are missing). I managed to get everything I need with only about 20 dependencies. This can even be used as a replacement for coroutines for easy parrallelism and now I actually really like async rust. I think the whole smol stack is really great.
This is how software evolve. Tokio is the defacto standard just like Spring was and is the defacto standard even when JEE was created. The best library will prevail. That's it.
Async is default feature of Rust. The async runtime (the scheduler) is not. Including runtime is a no no for embedded or RTOS. It's violate the zero-cost abstraction.
@@ardfard101 Which makes sense, because async will never be zero cost. It's simply impossible. People got up in arms about C++ async including a runtime and allocations but there's truly no way around it. If you wanna context switch in a userland perspective that's what has to happen. The truth is that if such a runtime is a no-no for embedded, I doubt there exists any suitable async runtime for embedded. Surely, including a runtime and then simply turning it off via the compiler would make more sense. Rust should've taken a more opinionated approach to this IMO. They can always take a page from C++ and just include Tokio in the standard library.
I haven't watched the video completely, here are my initial thoughts. The article being reviewed seems kinda whiney, there's many options to choose from but the most common is tokio. Unless if you have specific requirements for your asunc runtime you just use that. The alternative is having a single async runtime for the language which is very limiting. Library implementors choosing to support multiple runtimes is an unfortunate result of this choice. The complaints about async rust being difficult to write seem silly to me, asynchronous code is extremely complicated and error prone to write without rust's compile-time checks. It forces you to consider the real complexity of what asynchronous code actually involves and do it right. It's also silly to complain that it doesn't work in bare metal environments, because such environments cannot support OS threads, locks or other basic things that you need to correctly write asynchronous code. Asynchronous code aslo has massive overhead that you do not want in bare metal applications. It seems kind of complaining that the writer of the article can't have their cake and eat it too
There's the Async Rust book by Maxwell Flitton and Caroline Morton which is scheduled to be published in Dec 2024. That book uses Tokio. So I guess that Tokio will be the de-facto async thingy in Rust.
at 16:20 you talk about the reflexive "just _ ignore the error". This is actually one of the bad habits newbies need to learn to suppress. The whole point of the compiler telling you you didn't handle something is that you are almost certainly making a mistake, and you should take it seriously. Setting aside whether async programming and futures are bad - arguable - the habit of ignoring errors absolutely will destroy you.
My understanding of Rust’s async model is that the language should provide async variants of I/O methods that return Futures. It is then up to the developer to decide how to put the Futures together to build up a runtime. How did it happen that making an async web request requires the Tokio runtime? I feel like somewhere there was a breakdown in communication. I’m just getting acquainted with Rust, so pardon my naive question.
Rust currently provides the language constructs for async, e.g., the async/await syntax, and Futures, but not much else. Any asynchronous I/O is done using a library such as tokio, which provides synchronization primitives made to synchronize async tasks rather than threads, and I/O such as traits for asynchronously reading and writing files, and sockets that can be awaited instead of blocked on.
>My understanding of Rust’s async model is that the language should provide async variants of I/O methods that return Futures. This is not an ideal thing to add into std because those implementation are changing constantly.
"This one of the problems about Rust in general [...] they want you to know everything before you program anything" YUP. And what I've been doing instead is just ignoring this and starting to code shit. Like, I can simply decide that I'm going to write stuff and if I don't know enough yet, I'll certainly reach that bridge and decide whether I'm going to jump off it or not.
He's made negative comments about helix in so many of his videos already. Basically, his argument is that 1) it's not vim motions, and 2) you should use vim motions, and 3) vim motions have already won, everywhere, and you can use them everywhere
Is Box dyn error bad? I thought type erasure for error propagation is good? Especially considering how you also have the option of downcasting it back to the concrete error type after. Genuinely curious.
@@lowellthoerner1209 Right that makes sense. Though I think it's also fair to say that for most applications, the "happy path" doesn't actually result in an error so most of the time it does not even do vtable look up with Box
10ms seems long but that's probably for running a script from the shell. In a server configuration, php-fpm caches the bytecode of your program in RAM and on top of that you can enable JIT. I don't have a bench but it should be way faster than 10ms because locally I usually see 10ms time for a response with HTML templating and what not.
I'm guessing threads might also be faster in practice if you use thread pools for example
23 дні тому
"Progressive discovery" is probably the term for learning curve and managing complexity you're looking for. Instead of throwing a bunch of details and options at beginning users to allow them to do something, have simple wrappers, builders, and examples that do the right thing™ with sensible defaults. If one needs to control behavior or adjust options, there should be a way™to get at them.
I still can't believe someone in the Rust team was reasonable enough to think: "You know what, async programming is a core part of a language, it's so important that we should be careful with it. So we should definitely give the responsibility of this core part of this modern language to community"
Well it wasn't as obvious when Rust started. JS added async in 2017. TS in 2015. I wouldn't say async was obvious as a feature then. Rust started in 2010, first stable release 2015. It's a very new language. Not the best comparison of course. Rust has changed a lot in these short years I'm sure. But assuming JS drove the "obviousness" of async/await that's not a lot of time for a language to adapt. Especially considering the core of Rust is low level programming where async/await as a feature is more unusual and the large burden they've taken on with the borrowchecker.
@@0xCAFEF00D C# added async in 2012 and it based on... thread pool! And even in Rust-Book they teach how to create basic web server based on your own thread pool with channels lol. So they could just add default async std runtime implementation based on thread pool as good enough for a default one.
What a lot of these comments, like yours, don't seem to understand, is that people who work on Rust, and the people who work on the Rust ecosystem, are the same community of people. If there is ever a distinction, it's that those who work on Rust specifically are the ones who showed an interest in working on the language, showed up, and did the work. This is a truly open source project, not something shepherded by a company: I'm not making any value judgments by saying that, but the point is that it's not a "We" and "They", it's a "We" and "We".
@@nisonatic I think you and I actually agree on this. I was addressing OP's belief that there is a clear distinction between the Rust project, and the Rust library authors. I think it's a Venn diagram where the part that's exclusively the Rust project is a tiny sliver. In fact, it's because the Rust project wanted people to experiment with different async runtimes that we ended up in the situation we're in today. It was a conscious and deliberate decision, and one taken after a lot of thought and discussion, not some BDFL from the Rust Project that simply said "the users can go and build this core piece of our language", as OP characterises it. Not shipping an async runtime might be surprising to people, but it's actually unsurprising when you consider things like serde (serialisation and deserialisation), regex (regular expressions) and rand (random number generation) aren't part of the standard library. Granted it's slightly different, i.e. you can write programs that don't make use of any of those behaviours while you can't write async programs without an async runtime, but I see it as being not too different a situation as a crate that only ships a trait (see tower-service for an example).
23 дні тому
Multiple runtimes leads to fragmentation. Maybe have a default supported runtime with plugin support if it should be swapped out.
Channels use locks? I guess while waiting to recv, you're waiting on something that might as well be a lock if it isn't, but what's the alternative? Spin? If there's already something in the queue you don't wait on a lock, right? Isn't the whole idea of an mpsc queue that it's lock free? Can you elaborate on what the problems are with Box?
Thanks for this detailed video, I would just say, it's no coincidence that the Book does not talk about Async Rust yet, while it DOES talk about threads and channels. Just my thoughts. I would like to add that am still quite new to Rust and I do not use the language professionally yet, but I am really actively learning it, so things like reading the book, etc. was also quite recently for me, as well as the rustlings tests, etc. However I am a developer for about 20 years in a lot of other languages, with C# as probably the most important one. It the first one of all languages ever which introduced async await. Although the async await system in C# also knows it's serious caveats (like the deadlock risk in so called sync over async code, due to blocking), and it also comes with some performance and memory overhead for each thread too, it does not have that whole problem of Rust with the dependency on extra runtimes which was this video for a large part about. An async operation in C# is called a Task, but it's roughly the same thing as what other languages call a Promise or Future. The difference with Rust is that the task just starts directly after the async method is called, no extra runtime is needed to actually execute it, but all functionality is already built into in DotNet by default. So await really only says that further code execution should be suspended and non blockingly wait until the already running task is finished. Can anyone explain to me why people thought it was a good idea to follow the really different approach in Rust instead? I am curious about actual considerations.
I'm really curious what the actual overhead for scoped threads in std is... Does it keep some threadpool around or do you pay the price for new threads every time?
They don't really have a overhead. It just allows you to use non-static references in the scope because all created threads in the scope are guaranteed to finish when the scope ends. So instead of adding overhead it just restricts what you can do.
@@cocacodes But it does create new threads every time right? I want to parallelize my custom ECS, scoped threads would be perfect for that (I could just borrow a slice of my component arrays per thread), but if that means creating new threads every frame I would be better of using rayon's scoped thread no?
@@stysner4580 Yeah, scoped threads aren't really all that different from normal threads and so they don't have any thread pooling. I don't know much about rayon's internals though so you'd have to take a look at it yourselves (edit: I've taken a look at some stuff in rayon and it does seem like the ThreadPool struct has a scope method so using that could be ideal for you)
I’ve built a couple toy applications in Rust. They were only mildly annoying and the language has some cool features. But the more I learn about the types of artificial problems that come up purely as a matter of fighting with the compiler and ecosystem, it basically has ruled out ever using it on a production project for me.
what are you talking about man its all zero cost abstraction. Of course this doesn't include the 75% abstraction 'sin' tax. That isn't a cost. Its your duty an abstraction junkie.
This is specifically an async Rust issue. There are 2 main issues that I see: 1. the async-await feature was released half-baked, and they are implementing changes to the language that hopefully address the papercuts: it's basically code that people intuitively write that doesn't work (e.g. aliasing traits), should work in the future. 2. As a systems language, it doesn't ship its own async runtime (a conscious choice, for maximum flexibility), and it's academically a problem, but it's not practically a problem because you just pick Tokio and you move on. Sync Rust on the other hand is mature in many domains and an absolute joy to use. If you're building *applications* in many different domains, Rust is a joy to use. In some domains (front end web, gui applications, AAA games), Rust is still in its infancy. If you're building *libraries*, well then, that's when you actually really need to dig into the language, so that you can provide a nice API to your users. Just like in other languages, the library author feels the pain so the application developer can have a nice experience. If you aren't shipping maximally-flexible, maximally-performant, maximally-ergonomic libraries, then just use those libraries to build the applications.
I'd definitely give yourself more time to play with it, and spend less time listening to language nerds argue over idiosyncrasies that may or may not ever apply to you. We use Rust in production services and the fact that once it compiles you can have high confidence it's going to work is an amazingly freeing place to be. Well-architected async environments in Rust will not abuse you as this article might imply. YMMV.
I honestlz would made async single threaded so locking in not necessary and make people use threads if need speedup. Most people use concurrency not for speed or efficiency, but because of I/O.... Pretty much all of that can be handled with single threaded ops I think. Then for real perf maybe you want to handle threads anyways when being CPU bound instead...
Ya... When I use async rust I also get a little pissed about it. Coming from Go, it just seems like there was such an easier way than function colouring and having to have 2 functions for everything that can block. 1 that blocks and 1 that returns a future. I loved async in JS then I tried Go and saw how much nicer it could be, shame Rust when down the async path instead of goroutine path. Or even zig. Haven't done any async zig (on account of them removing it) but from the documentation zig also looks to be colourless, having to opt into async at call time by prepending the call with `async`
What’s the point of having the choices when 4/5 choices are bad for 80-90% of use cases. Just go with a good choice and if that isn’t sufficient have the community build something else. I’d prefer having a language be opinionated if the decisions are pragmatic in nature vs 🤷♂️.
With Rust, it just doesn't seem worth the time to really master it when languages like go which are basically 10-20% slower are so much easier to work with. That and I am bound to get cancelled if I ever really engaged with the community.
If I want to hire a bunch of developers to quickly develop a web service (whose code I'll never touch), I would choose Go. If I have to touch the code, I would choose Rust.
The main reason I love it is because it forces me to write good code. You can even force the compiler to prove that your program could literally never panic. It's a byproduct of good code that it is fast, not necessarily the draw. Sure, sometimes I end up in a 30minute battle with the borrow checker, but it's usually because I've had some misunderstanding about the data structure, and the moment I get my code to compile is the moment I fixed that misunderstanding. I can't really get away with not understanding, so the process of coding in rust always makes me better
I think the Rust bureau of language and doctorine broke the fantasy of Rust as a magically memory-safe language. To me it nowdays seems like a just another language that accepts a whole range of already known compromises. For the longest time the compromises, while obviously known by the community, were treated like rules of an arcane magic system. That if you just learned Rust deep enough you could bypass them and unlock the true potential of the language. That eventually someone would write an even better library that overcame the compromises in such elegant manner the developer would hardly notice them. Today they just feel like normal compromises. Something that introduces fundamental limitations, and you can’t defeat those limitations by anything as elegant as magic or alchemy. No matter how many projects you rewrite in rust, you won’t unlock arcane knowledge. No library will abstract the limitations away through magic.
i think its precisely NOT making comprises why people hate async rust. rust elects to give the programmer so much more control of futures than any other language, what other languages would even attempt to offer zero allocation async/await
Anyone that looked at Rust as "Magically doing things" was just wrong, the whole point of the language is to not be magical about things. I don't think anyone that read the actual documentation would come up with that perspective, but overhyped and shallow youtuber takes might indeed give that impression.
@@Tigregalis I'm on it. Axum + sqlx seem fairly trivial to use. After that i am making my web app logic in wasm with rust to practice some regular rust.
Maybe OS threads are faster with DDR5. Apparently there was a merge of a google patch for better OS threads into the kernel recently. I really donno. How do OS threads handle their memory, changes to memory and pass their results back to the originating process (thread)?
Why are we even bothering ourselves with the function coloring of async await? Also the api duplication. Wouldn't it be much easier and clearer if we would write every atomically async operation like io in an async fashion and have a "block()" function on the future that pulls it's async chain to an end? It's basically what the sync version is doing pausing the current function until the blocking operations in the called function are done. This way we wouldn't have bloated apis and people wouldn't need to async their code all the way up. A function only doing other async calls and awaiting each synchronously is useless. The power of context switching only works if multiple independent code blocks can be run as soon as another code block blocks (await).
@@minciNashu same as sync blocking with the only difference being is that the scheduler reuse the cpu while the async operation blocks. Async await is just automatic compile and runtime time refactoring. It's theoretically possible to write the same code in a sync style. But it would be a nightmare to write and read.
Rust could learn something from dinosaurs - Java/.NET First you reason about primitives - threads, executors, pools, locks, mutexes, channels. Then you reason about futures. And then you reason on async/await.
On channels... I had to work with rust / tokio channels recently, and it's unfortunate hiw different they are from go's channels; not easily useable for pure signaling. (Though honestly it seems like go's channels were only accidentally successful, since they weren't really designed for signalling either.)
Man, I thought JS was stupid, but more I see of rust, more I respect the madlads at v8,javascriptcore,node, bun, hermes etc who spend their life improving a language which is obviously flawed but managed to stay consistent with itself (even the quirks), while continuously improving performance. Rust is a good programming language, but IMHO it has the worst syntax and semantics of the currently popular language set.And most of the community is as toxic as a nuclear wasteland.
Curious about worst syntax and semantics of the currently popular language set. I assume you don't consider C++ a currently popular language as I think that is objectively (not subjectively: objectively), worse. I can understand how the syntax might feel daunting for a beginner (I was there), but there's nothing inherently more complex about it (except in so much as there are a larger set semantics that need to be expressed through syntax), it's just a lack of familiarity, and it's something you get used to. The syntax is very concise for the number of things you're actually declaring/doing, and maybe the issue for beginners is that it's too concise. Where in the world do you get the idea that the community is as toxic as a nuclear wasteland, though?
@@Tigregalisexactly. I actually think rust has one of the best syntax out of all languages. It’s so much easier to grasp and makes sense. The problems everyone has with rust usually isn’t the language itself but how they chose to handle stuff like async runtimes and also how the borrow checker still has major causes of false positives (code that SHOULD compile because it is 100% safe but the borrow checker does not perform a deep enough analysis )
@@lack_of_awareness Curious about this. Just a beginner here but it seems like if the borrow checker did a deeper analysis you'd end up in a situation where you'd have a whole bunch of exceptions to a rule. The code and the compiler's behaviour would be harder to reason about and reverse engineer (my 2nd favourite way of learning).
I'm not so impressed by Rust, developed 2 APIs and a basic OS in it. I find the syntax annoying and verbose and you have to 'bend' it constantly, 'meh'
I am not very familiar with Rust syntax but this does very much remind me of the async ecosystem in Python and the whole asyncio vs trio discussion. It can be extremely painful to work with threads in async Python and the fact that asyncio (the standard async runtime) is so obtuse to work with does not help at all.
Honestly this seems to be a killer for anything that needs parallelism which is basicly every hpc task. If rust wants to replace c++ having no good threading is extremely problematic
@Architector_4 the fact that parallelism is a runtime at all is the problem. If u compare this to c++ gpu programing (something u would think would be a runtime) u can move move device ptrs around between cuda openmp etc and that means u r not stuck with the choice ur library designer made. Essentially rust needs to get its act together around parallelism so we can start seeing it in hpc enviorments
@@nevokrien95Again, rust doesn't have any problems with parallelism. Threads are good, better than C++'s. What isn't all that nice is Async/Await, which requires a runtime regardless of language.
@vladlu6362 I don't understand why you need a separate runtime for async. Can't u achive this with a few macros? Also why the need for more than 1 of these
@@vladlu6362 ...Now you're saying that rust doesn't have any problems with parallelism? that was my initial point and i guess i'm glad that you agree; i argue nothing about async lol
@@Tigregalis, but these runtime libraries aren't additional libraries, but replacement runtimes. there should be a standard runtime, and feature add's should be libraries based on the standard runtime.
People seeks to find issues in rust, but the fact it's growing to fast and solving problems in industry that's next decade it will be the next default 😂
@Sam-cp6so What are you even saying? WASM is just a compilation target, one that brings the low level control of a systems language to a web browser. And the job of the language it to do a good job at that, regardless of the target you set LLVM to. People writing libraries and frameworks for one or the other changes nothing about that. Web frontends won't affect you if you are writing code for a microcontroller and highly performance conscious, byte counting, no-std libraries won't affect you if you are using a web framework.
Let's break it down. Hard? Yes, when applied for inherently difficult domains. However, Rust has very little incidental complexity. For simple use-cases, Rust is just as easy. Esoteric? The only "esoteric" thing about Rust itself is lifetimes. And it's better than hidden complexity related to lifetimes that comes in C++. Go has GC to deal with memory but no amount of tuning can fix it at scale. Useless? Rust is anything but useless. It can be used for nearly anything and is especially good in backend and cross-platform development. Go is nice and simple but it doesn't scale well due to GC. And you know what? Rust can be used from Go to solve some of its problems.
Its getting used, and to good effect. The downsides, to me, seem like they are mostly just the culture of people trying to stake out new architectural territory. We need those people to do that stuff, we just don't want to deal with the negative consequences of breaking new ground. So don't. Haskell and rust are at least useful as places to put new programming language concepts through their paces. Fortunately, they can do this just fine without you giving any shits or investing any time in them. Other people are more than willing to take on the risk for you. That is another good part about 'architectural gold rushes', they attract risk tolerant test pilots. Veterans don't need to kneel on a pire for progress.
For anything performance critical where you don't want to shoot yourself in the foot with dangling pointers and data races Rust is about as useful as a language can get...
Yeah you're a tokio Andy. As soon as I saw tokio I was disgusted, researched other ones and ended up using smol because all I needed was an event loop not a framework. Should probably just write your own if it's a production application, what you actually need is usually not that big and you don't have to deal with all the BS in tokio.
How so? I've seen no sign of that. (Writing embedded Rust with >50 simultaneous tasks and lots of interaction. No races seen yet, nor apparently the chance of any.)
To those who are interested in isolating async rust in a application you can do so by creating a creating a async runtime in a different thread from main. Then you can share data between the async runtime and your sync rust using ideally a channel or some global data.
In Tokio the `#[tokio::main]` macro is just a wrapper for the `tokio::runtime::Builder` which when built calls `Runtime::block_on`. You can easily do all of this inside a thread instead of main and have your own isolated async runtime.
This was the main thing I was wondering about. That's a very cool idea, thank you. :)
but how can i do work stealing on thousands of blocking tasks?
i can see building a threadpool that handles a queue of tasks or something of that sort in sync, but how do you tell the threads to switch tasks when they are waiting for something
@@eddierandom6074 This is not the most widely applicable architecture you can use. If you want to do something fully integrated like what you mentioned you simply need a async runtime everywhere for that.
The thing this architecture was originally suggested for was something like a discord clone where the sync side handles the user input and UI stuff while the async side handles the IO. I would definitely *not* suggest people consider this option unless they are making something like a single user application (and even then this isn't the ultimate solution a lot of the times).
This is just one way to capture the async beast if you were curious on how to do so.
@@eddierandom6074that's how Tokio multithreaded runtime works
_Furiously builds an Erlang-like "process" system for Rust._
The development of async-std slowed down because the rust team is working on providing that functionality as part of std::async. So, hopefully at some point in the future, we will see (most of) the ecosystem defragmentation go away - they will all use std::async without depending on a specific runtime.
The compiler team is working hard on implementing async traits as a language feature, which will allow the development of std::async to the standard of the rust std library.
@jonkoenig2478chatgpt said it
@jonkoenig2478Async traits are now available
I actually used to dislike async rust a lot. (Tokio is really overkill for most use cases imo, it also adds like 300 dependencies) But using async-executor etc. directly (basically the crates that smol is built ontop of) and futures-lite was a really nice experience (even tho some features are missing). I managed to get everything I need with only about 20 dependencies. This can even be used as a replacement for coroutines for easy parrallelism and now I actually really like async rust. I think the whole smol stack is really great.
This is how software evolve. Tokio is the defacto standard just like Spring was and is the defacto standard even when JEE was created. The best library will prevail. That's it.
Not including async as default feature of Rust's language was the biggest mistake, now it's a complete mess
Not quite. Adding more options can have more good sides than downsides.
Threads exist if you need them in the standard library. Just because it's not as convenient doesn't mean it doesn't exist.
Async is default feature of Rust. The async runtime (the scheduler) is not. Including runtime is a no no for embedded or RTOS. It's violate the zero-cost abstraction.
@@ardfard101 Which makes sense, because async will never be zero cost. It's simply impossible. People got up in arms about C++ async including a runtime and allocations but there's truly no way around it. If you wanna context switch in a userland perspective that's what has to happen. The truth is that if such a runtime is a no-no for embedded, I doubt there exists any suitable async runtime for embedded. Surely, including a runtime and then simply turning it off via the compiler would make more sense. Rust should've taken a more opinionated approach to this IMO. They can always take a page from C++ and just include Tokio in the standard library.
@random_bitthere is async with no_std for embedded, but the implementation is much different.
I haven't watched the video completely, here are my initial thoughts.
The article being reviewed seems kinda whiney, there's many options to choose from but the most common is tokio. Unless if you have specific requirements for your asunc runtime you just use that. The alternative is having a single async runtime for the language which is very limiting. Library implementors choosing to support multiple runtimes is an unfortunate result of this choice. The complaints about async rust being difficult to write seem silly to me, asynchronous code is extremely complicated and error prone to write without rust's compile-time checks. It forces you to consider the real complexity of what asynchronous code actually involves and do it right. It's also silly to complain that it doesn't work in bare metal environments, because such environments cannot support OS threads, locks or other basic things that you need to correctly write asynchronous code. Asynchronous code aslo has massive overhead that you do not want in bare metal applications. It seems kind of complaining that the writer of the article can't have their cake and eat it too
The biggest valid complaint is that this should be baked into the language.
There's the Async Rust book by Maxwell Flitton and Caroline Morton which is scheduled to be published in Dec 2024. That book uses Tokio. So I guess that Tokio will be the de-facto async thingy in Rust.
at 16:20 you talk about the reflexive "just _ ignore the error". This is actually one of the bad habits newbies need to learn to suppress. The whole point of the compiler telling you you didn't handle something is that you are almost certainly making a mistake, and you should take it seriously. Setting aside whether async programming and futures are bad - arguable - the habit of ignoring errors absolutely will destroy you.
My understanding of Rust’s async model is that the language should provide async variants of I/O methods that return Futures. It is then up to the developer to decide how to put the Futures together to build up a runtime. How did it happen that making an async web request requires the Tokio runtime? I feel like somewhere there was a breakdown in communication. I’m just getting acquainted with Rust, so pardon my naive question.
Rust currently provides the language constructs for async, e.g., the async/await syntax, and Futures, but not much else. Any asynchronous I/O is done using a library such as tokio, which provides synchronization primitives made to synchronize async tasks rather than threads, and I/O such as traits for asynchronously reading and writing files, and sockets that can be awaited instead of blocked on.
>My understanding of Rust’s async model is that the language should provide async variants of I/O methods that return Futures.
This is not an ideal thing to add into std because those implementation are changing constantly.
You can tell Prime is a very trustworthy and reliable person by how transparent he is.
"This one of the problems about Rust in general [...] they want you to know everything before you program anything" YUP. And what I've been doing instead is just ignoring this and starting to code shit. Like, I can simply decide that I'm going to write stuff and if I don't know enough yet, I'll certainly reach that bridge and decide whether I'm going to jump off it or not.
@prime Have you done a video on helix yet?
He's made negative comments about helix in so many of his videos already. Basically, his argument is that 1) it's not vim motions, and 2) you should use vim motions, and 3) vim motions have already won, everywhere, and you can use them everywhere
Is Box dyn error bad? I thought type erasure for error propagation is good? Especially considering how you also have the option of downcasting it back to the concrete error type after. Genuinely curious.
Dynamic dispatch has a performance impact. Though, realistically, it's not going to be major. To my knowledge, Anyhow uses Box in the backend.
@@lowellthoerner1209 Right that makes sense.
Though I think it's also fair to say that for most applications, the "happy path" doesn't actually result in an error so most of the time it does not even do vtable look up with Box
@@lowellthoerner1209 Yes, but error handling is the slow path anyway.
10ms seems long but that's probably for running a script from the shell. In a server configuration, php-fpm caches the bytecode of your program in RAM and on top of that you can enable JIT. I don't have a bench but it should be way faster than 10ms because locally I usually see 10ms time for a response with HTML templating and what not.
I'm guessing threads might also be faster in practice if you use thread pools for example
"Progressive discovery" is probably the term for learning curve and managing complexity you're looking for.
Instead of throwing a bunch of details and options at beginning users to allow them to do something, have simple wrappers, builders, and examples that do the right thing™ with sensible defaults.
If one needs to control behavior or adjust options, there should be a way™to get at them.
2:00 That's actually why I like the sender and receiver proposal for C++.
I still can't believe someone in the Rust team was reasonable enough to think:
"You know what, async programming is a core part of a language, it's so important that we should be careful with it. So we should definitely give the responsibility of this core part of this modern language to community"
Well it wasn't as obvious when Rust started.
JS added async in 2017. TS in 2015. I wouldn't say async was obvious as a feature then.
Rust started in 2010, first stable release 2015. It's a very new language. Not the best comparison of course. Rust has changed a lot in these short years I'm sure. But assuming JS drove the "obviousness" of async/await that's not a lot of time for a language to adapt. Especially considering the core of Rust is low level programming where async/await as a feature is more unusual and the large burden they've taken on with the borrowchecker.
@@0xCAFEF00D C# added async in 2012 and it based on... thread pool! And even in Rust-Book they teach how to create basic web server based on your own thread pool with channels lol. So they could just add default async std runtime implementation based on thread pool as good enough for a default one.
What a lot of these comments, like yours, don't seem to understand, is that people who work on Rust, and the people who work on the Rust ecosystem, are the same community of people. If there is ever a distinction, it's that those who work on Rust specifically are the ones who showed an interest in working on the language, showed up, and did the work. This is a truly open source project, not something shepherded by a company: I'm not making any value judgments by saying that, but the point is that it's not a "We" and "They", it's a "We" and "We".
@@nisonatic I think you and I actually agree on this. I was addressing OP's belief that there is a clear distinction between the Rust project, and the Rust library authors. I think it's a Venn diagram where the part that's exclusively the Rust project is a tiny sliver.
In fact, it's because the Rust project wanted people to experiment with different async runtimes that we ended up in the situation we're in today. It was a conscious and deliberate decision, and one taken after a lot of thought and discussion, not some BDFL from the Rust Project that simply said "the users can go and build this core piece of our language", as OP characterises it.
Not shipping an async runtime might be surprising to people, but it's actually unsurprising when you consider things like serde (serialisation and deserialisation), regex (regular expressions) and rand (random number generation) aren't part of the standard library. Granted it's slightly different, i.e. you can write programs that don't make use of any of those behaviours while you can't write async programs without an async runtime, but I see it as being not too different a situation as a crate that only ships a trait (see tower-service for an example).
Multiple runtimes leads to fragmentation. Maybe have a default supported runtime with plugin support if it should be swapped out.
Channels use locks? I guess while waiting to recv, you're waiting on something that might as well be a lock if it isn't, but what's the alternative? Spin? If there's already something in the queue you don't wait on a lock, right? Isn't the whole idea of an mpsc queue that it's lock free?
Can you elaborate on what the problems are with Box?
The name.......... the AsyncRustagen
Thanks for this detailed video, I would just say, it's no coincidence that the Book does not talk about Async Rust yet, while it DOES talk about threads and channels. Just my thoughts.
I would like to add that am still quite new to Rust and I do not use the language professionally yet, but I am really actively learning it, so things like reading the book, etc. was also quite recently for me, as well as the rustlings tests, etc.
However I am a developer for about 20 years in a lot of other languages, with C# as probably the most important one. It the first one of all languages ever which introduced async await.
Although the async await system in C# also knows it's serious caveats (like the deadlock risk in so called sync over async code, due to blocking), and it also comes with some performance and memory overhead for each thread too, it does not have that whole problem of Rust with the dependency on extra runtimes which was this video for a large part about.
An async operation in C# is called a Task, but it's roughly the same thing as what other languages call a Promise or Future. The difference with Rust is that the task just starts directly after the async method is called, no extra runtime is needed to actually execute it, but all functionality is already built into in DotNet by default.
So await really only says that further code execution should be suspended and non blockingly wait until the already running task is finished.
Can anyone explain to me why people thought it was a good idea to follow the really different approach in Rust instead? I am curious about actual considerations.
Man yelling on top oh his lungs "TOKIOOOOO" almost every other video ~4 months ago questions whether he is a Tokio andy?
I am
Alright, so this guy just hates Tokio and wishes a different runtime had won?
Better article title: Pick Tokio and Don't Even Think About It
What color is your function became what color is your function and runtime...
I'm really curious what the actual overhead for scoped threads in std is... Does it keep some threadpool around or do you pay the price for new threads every time?
They don't really have a overhead. It just allows you to use non-static references in the scope because all created threads in the scope are guaranteed to finish when the scope ends. So instead of adding overhead it just restricts what you can do.
@@cocacodes But it does create new threads every time right? I want to parallelize my custom ECS, scoped threads would be perfect for that (I could just borrow a slice of my component arrays per thread), but if that means creating new threads every frame I would be better of using rayon's scoped thread no?
@@stysner4580 Yeah, scoped threads aren't really all that different from normal threads and so they don't have any thread pooling.
I don't know much about rayon's internals though so you'd have to take a look at it yourselves (edit: I've taken a look at some stuff in rayon and it does seem like the ThreadPool struct has a scope method so using that could be ideal for you)
@CocaBot Yeah rayon uses pools. Thank you!
2:36 I’m so sorry for you Flip. Stay strong
2:50 future-proof decisions KEKW
I’ve built a couple toy applications in Rust. They were only mildly annoying and the language has some cool features.
But the more I learn about the types of artificial problems that come up purely as a matter of fighting with the compiler and ecosystem, it basically has ruled out ever using it on a production project for me.
what are you talking about man its all zero cost abstraction. Of course this doesn't include the 75% abstraction 'sin' tax. That isn't a cost. Its your duty an abstraction junkie.
@@homelessrobot
"its all zero cost abstraction"
C++ devs said this once upon a time.
Ill agree with you depending on your answer to "do you use a memory safe language" (you better say yes 😛)
This is specifically an async Rust issue. There are 2 main issues that I see: 1. the async-await feature was released half-baked, and they are implementing changes to the language that hopefully address the papercuts: it's basically code that people intuitively write that doesn't work (e.g. aliasing traits), should work in the future. 2. As a systems language, it doesn't ship its own async runtime (a conscious choice, for maximum flexibility), and it's academically a problem, but it's not practically a problem because you just pick Tokio and you move on.
Sync Rust on the other hand is mature in many domains and an absolute joy to use.
If you're building *applications* in many different domains, Rust is a joy to use. In some domains (front end web, gui applications, AAA games), Rust is still in its infancy.
If you're building *libraries*, well then, that's when you actually really need to dig into the language, so that you can provide a nice API to your users. Just like in other languages, the library author feels the pain so the application developer can have a nice experience. If you aren't shipping maximally-flexible, maximally-performant, maximally-ergonomic libraries, then just use those libraries to build the applications.
I'd definitely give yourself more time to play with it, and spend less time listening to language nerds argue over idiosyncrasies that may or may not ever apply to you. We use Rust in production services and the fact that once it compiles you can have high confidence it's going to work is an amazingly freeing place to be. Well-architected async environments in Rust will not abuse you as this article might imply. YMMV.
reminded of the Boehm paper about pthreads and not implementing threads as a library again
I honestlz would made async single threaded so locking in not necessary and make people use threads if need speedup. Most people use concurrency not for speed or efficiency, but because of I/O.... Pretty much all of that can be handled with single threaded ops I think. Then for real perf maybe you want to handle threads anyways when being CPU bound instead...
Ya... When I use async rust I also get a little pissed about it. Coming from Go, it just seems like there was such an easier way than function colouring and having to have 2 functions for everything that can block. 1 that blocks and 1 that returns a future. I loved async in JS then I tried Go and saw how much nicer it could be, shame Rust when down the async path instead of goroutine path. Or even zig. Haven't done any async zig (on account of them removing it) but from the documentation zig also looks to be colourless, having to opt into async at call time by prepending the call with `async`
What about Zig's Async ?
Let's look at Paul Allen's async
I can't imagine it's 10 milliseconds if you're using threads properly with something like a thread pool idling
The editor knew what hes doing when that blur was added
3 whiskies in, watching this, had me giggling like a school girl. Accurate.
I like me that. 7:48 start overkill ..seems natural
The name is ... Asyncagent
To avoid leak, make async code call the sync code, not the other way around.
Editor, did that ending Sherlock Holmes factoid absolutely need to be in the video lmfao
This is where I like Java where libraries are backed by orgs like Apache, Eclipse foundation, Redhat etc
Me too 🎉
funnily enough I was reading up on scoped threads earlier on and channels too lol
The point of other async runtimes was to ensure tokio was indeed worthy of keeping. It passed the test. Now you can all chill and relax.
What’s the point of having the choices when 4/5 choices are bad for 80-90% of use cases. Just go with a good choice and if that isn’t sufficient have the community build something else. I’d prefer having a language be opinionated if the decisions are pragmatic in nature vs 🤷♂️.
This blogs looks very similar to the Oracle blog on Java virtual threads
With Rust, it just doesn't seem worth the time to really master it when languages like go which are basically 10-20% slower are so much easier to work with. That and I am bound to get cancelled if I ever really engaged with the community.
where you getting those figures from? for most non trivial applications this is not even close
If I want to hire a bunch of developers to quickly develop a web service (whose code I'll never touch), I would choose Go. If I have to touch the code, I would choose Rust.
Yet another person who thinks the main reason to use Rust is performance. Better performance is just gravy, not the point for almost anyone.
The main reason I love it is because it forces me to write good code. You can even force the compiler to prove that your program could literally never panic. It's a byproduct of good code that it is fast, not necessarily the draw. Sure, sometimes I end up in a 30minute battle with the borrow checker, but it's usually because I've had some misunderstanding about the data structure, and the moment I get my code to compile is the moment I fixed that misunderstanding. I can't really get away with not understanding, so the process of coding in rust always makes me better
I think the Rust bureau of language and doctorine broke the fantasy of Rust as a magically memory-safe language. To me it nowdays seems like a just another language that accepts a whole range of already known compromises.
For the longest time the compromises, while obviously known by the community, were treated like rules of an arcane magic system. That if you just learned Rust deep enough you could bypass them and unlock the true potential of the language. That eventually someone would write an even better library that overcame the compromises in such elegant manner the developer would hardly notice them.
Today they just feel like normal compromises. Something that introduces fundamental limitations, and you can’t defeat those limitations by anything as elegant as magic or alchemy.
No matter how many projects you rewrite in rust, you won’t unlock arcane knowledge. No library will abstract the limitations away through magic.
i think its precisely NOT making comprises why people hate async rust. rust elects to give the programmer so much more control of futures than any other language, what other languages would even attempt to offer zero allocation async/await
Anyone that looked at Rust as "Magically doing things" was just wrong, the whole point of the language is to not be magical about things. I don't think anyone that read the actual documentation would come up with that perspective, but overhyped and shallow youtuber takes might indeed give that impression.
Less rhetorically, more technically, what are you referring to, exactly?
Hey, Rust, give us standard non-blocking I/O functions and we will do the rest.
I really like Flip's mid-video comments 😂
SHOULD I WRITE MY WEB API ON RUST OR NOT I AM SO CONFUSED RN.
Yes. Try Axum.
@@Tigregalis I'm on it. Axum + sqlx seem fairly trivial to use.
After that i am making my web app logic in wasm with rust to practice some regular rust.
Architects are stuck perpetually at whiteboards.
Maybe OS threads are faster with DDR5. Apparently there was a merge of a google patch for better OS threads into the kernel recently.
I really donno. How do OS threads handle their memory, changes to memory and pass their results back to the originating process (thread)?
Why are we even bothering ourselves with the function coloring of async await? Also the api duplication.
Wouldn't it be much easier and clearer if we would write every atomically async operation like io in an async fashion and have a "block()" function on the future that pulls it's async chain to an end? It's basically what the sync version is doing pausing the current function until the blocking operations in the called function are done. This way we wouldn't have bloated apis and people wouldn't need to async their code all the way up. A function only doing other async calls and awaiting each synchronously is useless. The power of context switching only works if multiple independent code blocks can be run as soon as another code block blocks (await).
The caller of await is suspended until await returns.
@@minciNashu same as sync blocking with the only difference being is that the scheduler reuse the cpu while the async operation blocks. Async await is just automatic compile and runtime time refactoring. It's theoretically possible to write the same code in a sync style. But it would be a nightmare to write and read.
Half (well..) the fun of these arise from the twitch remarks
Rust could learn something from dinosaurs - Java/.NET
First you reason about primitives - threads, executors, pools, locks, mutexes, channels.
Then you reason about futures.
And then you reason on async/await.
Prefer channels over mutex, please. Even the book mentions it first.
I feel like the author of that article should be locked in a room with monkeys that code JavaScript for eternity
7:39 place "neovim" instead "async" 🙂 and the rest like milky to a kitten
Heres for the algae of rhythm :)
i cant ghet in anymore
On channels... I had to work with rust / tokio channels recently, and it's unfortunate hiw different they are from go's channels; not easily useable for pure signaling.
(Though honestly it seems like go's channels were only accidentally successful, since they weren't really designed for signalling either.)
I don't care enough. If I need async in rust, tokio is great.
vite is a french word, so they say
Man, I thought JS was stupid, but more I see of rust, more I respect the madlads at v8,javascriptcore,node, bun, hermes etc who spend their life improving a language which is obviously flawed but managed to stay consistent with itself (even the quirks), while continuously improving performance. Rust is a good programming language, but IMHO it has the worst syntax and semantics of the currently popular language set.And most of the community is as toxic as a nuclear wasteland.
Curious about worst syntax and semantics of the currently popular language set. I assume you don't consider C++ a currently popular language as I think that is objectively (not subjectively: objectively), worse.
I can understand how the syntax might feel daunting for a beginner (I was there), but there's nothing inherently more complex about it (except in so much as there are a larger set semantics that need to be expressed through syntax), it's just a lack of familiarity, and it's something you get used to. The syntax is very concise for the number of things you're actually declaring/doing, and maybe the issue for beginners is that it's too concise.
Where in the world do you get the idea that the community is as toxic as a nuclear wasteland, though?
All the credit of js goes to Zig and C++ and the geniouses who write genious code with thosr to make JS into what it is.
@@Tigregalisexactly. I actually think rust has one of the best syntax out of all languages. It’s so much easier to grasp and makes sense. The problems everyone has with rust usually isn’t the language itself but how they chose to handle stuff like async runtimes and also how the borrow checker still has major causes of false positives (code that SHOULD compile because it is 100% safe but the borrow checker does not perform a deep enough analysis )
@@lack_of_awareness
Curious about this.
Just a beginner here but it seems like if the borrow checker did a deeper analysis you'd end up in a situation where you'd have a whole bunch of exceptions to a rule. The code and the compiler's behaviour would be harder to reason about and reverse engineer (my 2nd favourite way of learning).
tokio-console is your friend.
Learnt scoped threads and channels first. Much simpler
CHROOT!
I'm not so impressed by Rust, developed 2 APIs and a basic OS in it. I find the syntax annoying and verbose and you have to 'bend' it constantly, 'meh'
Pff easy, I just have to never try using it and instead imagine that it would work if I ever tried.
Think twice before using Rust
I am not very familiar with Rust syntax but this does very much remind me of the async ecosystem in Python and the whole asyncio vs trio discussion. It can be extremely painful to work with threads in async Python and the fact that asyncio (the standard async runtime) is so obtuse to work with does not help at all.
Can't wait until the Linux kernel has more of it written in Rust.
19:50 ConcurrEnt - concurrEncy. It's not that hard.
Honestly this seems to be a killer for anything that needs parallelism which is basicly every hpc task.
If rust wants to replace c++ having no good threading is extremely problematic
this talks about async, not threading? rust has fine threading as far as i know lol
@Architector_4 the fact that parallelism is a runtime at all is the problem.
If u compare this to c++ gpu programing (something u would think would be a runtime) u can move move device ptrs around between cuda openmp etc and that means u r not stuck with the choice ur library designer made.
Essentially rust needs to get its act together around parallelism so we can start seeing it in hpc enviorments
@@nevokrien95Again, rust doesn't have any problems with parallelism. Threads are good, better than C++'s. What isn't all that nice is Async/Await, which requires a runtime regardless of language.
@vladlu6362 I don't understand why you need a separate runtime for async.
Can't u achive this with a few macros?
Also why the need for more than 1 of these
@@vladlu6362
...Now you're saying that rust doesn't have any problems with parallelism?
that was my initial point and i guess i'm glad that you agree; i argue nothing about async lol
Why tf are there diff runtimes?? Sounds like a terrible ecosystem.
Why not provide these features in a library instead of a new runtime?
@@paherbst524they are literally libraries though
I'm on embedded. You couldn't have a standard Rust async runtime that would suit me as well, at least not before another decade of Rust evolution.
To be clear:
1. Rust the language has no async runtime.
2. The runtimes are provided by the ecosystem.
3. These runtimes are libraries.
@@Tigregalis, but these runtime libraries aren't additional libraries, but replacement runtimes. there should be a standard runtime, and feature add's should be libraries based on the standard runtime.
You have no business posting rust content.
first daddy
People seeks to find issues in rust, but the fact it's growing to fast and solving problems in industry that's next decade it will be the next default 😂
Oh i have no doubt personally. Its' kind of a dubious honor though.
@Sam-cp6so
What are you even saying?
WASM is just a compilation target, one that brings the low level control of a systems language to a web browser. And the job of the language it to do a good job at that, regardless of the target you set LLVM to.
People writing libraries and frameworks for one or the other changes nothing about that. Web frontends won't affect you if you are writing code for a microcontroller and highly performance conscious, byte counting, no-std libraries won't affect you if you are using a web framework.
Rust, the new haskell: hard, exoteric and almost useless. No thanks, wont even invest my time, Go for the win all the way.
🙃
Let's break it down.
Hard? Yes, when applied for inherently difficult domains. However, Rust has very little incidental complexity. For simple use-cases, Rust is just as easy.
Esoteric? The only "esoteric" thing about Rust itself is lifetimes. And it's better than hidden complexity related to lifetimes that comes in C++. Go has GC to deal with memory but no amount of tuning can fix it at scale.
Useless? Rust is anything but useless. It can be used for nearly anything and is especially good in backend and cross-platform development. Go is nice and simple but it doesn't scale well due to GC. And you know what? Rust can be used from Go to solve some of its problems.
Just don't use rust for everything it's that simple
Choose language according to your project requirements not the other way around
Its getting used, and to good effect. The downsides, to me, seem like they are mostly just the culture of people trying to stake out new architectural territory. We need those people to do that stuff, we just don't want to deal with the negative consequences of breaking new ground. So don't. Haskell and rust are at least useful as places to put new programming language concepts through their paces.
Fortunately, they can do this just fine without you giving any shits or investing any time in them. Other people are more than willing to take on the risk for you. That is another good part about 'architectural gold rushes', they attract risk tolerant test pilots. Veterans don't need to kneel on a pire for progress.
For anything performance critical where you don't want to shoot yourself in the foot with dangling pointers and data races Rust is about as useful as a language can get...
Yeah you're a tokio Andy. As soon as I saw tokio I was disgusted, researched other ones and ended up using smol because all I needed was an event loop not a framework. Should probably just write your own if it's a production application, what you actually need is usually not that big and you don't have to deal with all the BS in tokio.
did you write this article?
Async in Rust is not safe. It's full of data races
How so? I've seen no sign of that. (Writing embedded Rust with >50 simultaneous tasks and lots of interaction. No races seen yet, nor apparently the chance of any.)
Data races are prevented by the type system