5 ms·
Color functions are just not the right road to go down. Something akin to Javas new green threads would be better, or Go style coroutines. The path Rust is goi
by no_wizard 3y ago
Color functions are just not the right road to go down. Something akin to Javas new green threads would be better, or Go style coroutines.
The path Rust is going means async becomes viral, and is something I dislike a lot about JavaScript[0] and other languages I’ve worked in[1].
I’d love to see Rust avoid this trap.
[0]: I work in TypeScript in actuality not sure which to use here. It’s certainly by far the language I’ve used the most in my career now.
[1]: I remember it infected Python too and it was a pain as well when I did Python development years ago.
- steveklabnik 3y agoDo you have a way to accomplish this while still staying within the other various constraints that Rust's async is under? I don't believe it is reconcilable. For those not aware of the history and looking for background, I laid it out here: https://www.infoq.com/presentations/rust-2019/ https://www.infoq.com/presentations/rust-2019/ and here https://www.infoq.com/presentations/rust-async-await/ https://www.infoq.com/presentations/rust-async-await/ Those style systems are useful and have advantages, but they also have disadvantages. Not every tradeoff is a good call for every system, and that goes both ways in this scenario.
- no_wizard 3y agoMake it an optional crate like `no-std` is, where you specify it in your cargo.toml
- shadowgovt 3y agoAt this time, I believe Rust's approach is complicated but that's because correctly using "bare" threads is complicated. Goroutines simplify the problem but introduce runtime performance overhead that may not be suitable for applications Rust is used for. (Generally, I avoid this problem these days by avoiding threads in favor of other abstractions or multiple processes communicating over an RPC channel).
- shellac 3y agoFor many languages I absolutely agree: stackful coroutines are the way to go since the programmer experience is much smoother, with fewer hiccups like having different kinds of functions, or being unable to yield in loops. Lua, Go, and now Java got this right; python, javascript, and c# have to live with a bit of a mess. But Rust is not a language which can dictate its execution environment. It needs to be able to exist in a C-ish world, and that's not something that supports yielding. It's a shame, but at least you can write kernel modules in Rust.
- thesuperbigfrog 3y ago>> But Rust is not a language which can dictate its execution environment. It needs to be able to exist in a C-ish world, and that's not something that supports yielding. It's a shame, but at least you can write kernel modules in Rust. Ada is also used in embedded and low-level environments where the execution environment can be limited. The way that Ada "gets around" such limitations is through language "annexes". Annexes are optional language extensions for specialized use cases: https://www.cambridge.org/core/books/abs/programming-in-ada-2012/specialized-annexes/A052B2FE3D0E1B1CF3B0FD2C4F106349 https://www.cambridge.org/core/books/abs/programming-in-ada-... http://www.ada-auth.org/standards/22rm/html/RM-1-1-2.html http://www.ada-auth.org/standards/22rm/html/RM-1-1-2.html Rust partially does this already with [no_std] (https://docs.rust-embedded.org/book/intro/no-std.html https://docs.rust-embedded.org/book/intro/no-std.html), so the concept is not too different.
- nu11ptr 3y agoWhile I agree and would prefer green threads to async myself, I believe Rust chose async/await because it was shown the former could not be done as a "zero cost" abstraction and didn't interop with C well (somebody correct me if I'm wrong on that).
- gpderetta 3y agoI vastly prefer stackful coroutines than stackless, but of all the languages, rust is probably the one that can justify that decision the most.
- littlestymaar 3y agoI really wish people stopped using this concept of “function colors”, especially in the context of Rust because the `async fn`/“regular function” split is strictly equivalent to the `try_something()`/`something()` split (the first one being fallible and returning a `Result` in case of failure). `Result`s and `Option`s are coloring the stack in exactly the same way a `Future` does (and `async` is pure syntactic sugar on top of future). So, someone may like exceptions and green threads more than `Result` and `async` (and this is a completely valid PoV, even though I personally like the explicitness better), but thinking `async` is somehow special is just a conceptual mistake. Edit to give an little more substance to the parallelism: If you want to call a fallible function inside an infallible one, you MUST handle the result. If you want to use `?` then your function MUST return a Result. Symmetrically, if you want to call an async function from a non-async one, you MUST `spawn` the future. If you want to use `await` then your function MUST be async. The only practical difference between async functions and functions returning a `Result` is that `Future` is a trait, not a struct like `Result` (and that means that your future may have a lifetime that's not visible in your function definition, which is an endless source of confusion for beginners).
- kaba0 3y agoWhile you are technically right, one area where the two are unlike is that you want to be polymorph with regards to async/non-async, while it is less often the case for error handling. That exact same sleep implementation, or db query should be usable both as an async call, as well as a blocking one and it is the caller that wants to decide that. Which is not trivial to do on the calling side from a language perspective as it then has to recursively decide the same for all its subcalls. The new experimental languages with effect types might be able to give us the best of both as they actually expose what you are talking about as an abstraction at the type level. We will see.
- ithkuil 3y agoYes. To make this more concrete: If your non-async function uses a parameter whose type is a dyn trait or a generic bounded by a trait, it can call the methods of that trait and thus potentially call different implementations of that trait. Currently, none of those trait implementations can be async because that would change the function signature. So the only option you have in a trait implementation that needs to call a library that happens to use async apis internallt, is to "block_on" the async. Unfortunately iirc blocking on an async like that is executor specific and your impl must pick a concrete async executor which may be different from the one used elsewhere in the program.
- kaba0 3y ago> Color functions are just not the right road to go down. Something akin to Javas new green threads would be better, or Go style coroutines I agree with you on this in case of high-level languages. Rust is not that, and wouldn’t be half as interesting that way — but by going the system/low-level language route it does have to make certain design decisions that are not ideal. They can’t do what java’s loom does as it requires knowing every method implementation, which is fine with a fat runtime, but is not possible in case of Rust, with plenty FFI boundaries, etc.
- hgomersall 3y ago"Color" in this case just means signature. Of course you can't mix signatures - why would you want to? You might as well argue that Rust shouldn't have had any more than a single integer argument to every function. Async in rust essentially desugars to a return trait impl.
- zaphar 3y agoThat isn't quite true. An async signature forces it's caller to be async as well. This is because of it's semantics. Most signatures don't infect their callers signatures like that. async is qualitatively different in this regard.
- hgomersall 3y agoNo it doesn't. Your can call an async function in rust from anywhere, you just can't await on it outside of an async function.
- zaphar 3y agoThe async function also can't await outside of an async runtime. Which means I cant just call it and expect it to work. I need to wrap the call in an executor of some sort. If the async function doesn't do any awaiting itself then it doesn't even need to be an async function. So in practice my statement stands.
- hgomersall 3y agoBut that's true of everything. You can't use a struct except through an "executor" of some sort. Once you've created it, it sits there and does nothing until you exercise it through its methods or other methods that take it. In that case, there's no ambiguity because the type is explicit. I guess the problem is that really `async fn` looks like a function, but isn't really a function, it's a future. The semantics of a future are quite different to the semantics of a function. That's only a problem if you think the semantics of `async fn` should closely reflect a function. The thing is, once you grok async in rust, other things make sense, like being able to construct futures by implementing `Future` on a struct. You don't actually need `async fn` to do async rust. It's just syntactic sugar, just like await is.