Earlier quoted context omitted.
Go can do this because it’s closer to Java or C# than C. It has a runtime that gets compiled into every binary. Rust deliberately avoided that by design.
Indeed. That means that the interop story for golang is horrible. Golang can somewha work with libraries with C bindings. But you can not publish a golang lib as a lib.so with C bindings because of the runtime.
Local async executors and why they should be the default
111–120 of 327 posts
Re: Local async executors and why they should be the default
#112Earlier quoted context omitted.
Go can do this because it’s closer to Java or C# than C. It has a runtime that gets compiled into every binary. Rust deliberately avoided that by design.
Why is runtime needed for a syntactic feature like that? All you need is an async wrapper for system calls, and every sync function could, in principle, be compiled down to a coroutine and ran on an executor of your choice. Just because Go has a runtime and has a go keyword, doesn't in itself mean that runtime is necessary for the go keyword to exist.
Re: Local async executors and why they should be the default
#113Just a personal take: after not coding with Rust for several months, I find it more and more difficult to return to an async code I was writing. The whole thing just reads... ugly and inconsistent. It needs too much already-accumulated knowledge. As the article correctly points out, you need a bunch of stuff that are seemingly unrelated (and syntactically speaking you would never guess they belong together). And as o…
Think about it. Where did it come from in its current incarnation? Node. Why did Node choose it? Did it have a multiplicity of options and carefully choose the best one based on years of experience with each choice? No. Async was "chosen" because it was the only option for the weak runtime that it had to work with. It was the only choice.
But the Node marketing machine rumbled into action and made bold claims about performance and quality and phrased them in universals across all languages rather than merely claiming it was good for Javascript and that propaganda is still running around in people's minds to this day.
I know Rust chose it for performance reasons but I still think in a decade or two community consensus will be either that the 5% performance overhead is worth it for any non-trivial program because async is just too much worse, or someone will come up with an even lower impact version of some other option that will dominate it.
As your message demonstrates, we're still in the phase where you have to pay lip service to the supposed community consensus before saying "but I think it isn't really working out", but as I don't care about the community consensus I don't mind just saying it. Async is a mistake. It's just you manually doing what the compiler ought to be doing for you, and while I'm open to other possibilities in the future, right now threaded code is straight-up a better option on almost every metric, and the metrics where it may have a slight disadvantage, it's only slight and worth it. It's not like you choose threading and you instantly lose 10x performance or something. The first and second derivative opinions about async are clearly negative and I don't mind jumping farther ahead in the process when it comes to my own engineering decisions.
Re: Local async executors and why they should be the default
#114Earlier quoted context omitted.
Eh, in that case, you can also just write single or multithreaded Rust code, entirely ignoring async Rust. I do. And multithreaded Rust is much more pleasant experience than multithreaded C++. Which dependency is forcing async Rust on you? Alternatives are usually available.
Postgres (which is a wrapper around tokio-postgres, and as a result drags the whole tokio bloated dependency graph). I would love to have a purely sync alternative to that.
Re: Local async executors and why they should be the default
#115Just a personal take: after not coding with Rust for several months, I find it more and more difficult to return to an async code I was writing. The whole thing just reads... ugly and inconsistent. It needs too much already-accumulated knowledge. As the article correctly points out, you need a bunch of stuff that are seemingly unrelated (and syntactically speaking you would never guess they belong together). And as o…
I really think in the end, in another decade or two, the community consensus is going to be that async as it is conceived of today is simply a mistake, full stop. Think about it. Where did it come from in its current incarnation? Node. Why did Node choose it? Did it have a multiplicity of options and carefully choose the best one based on years of experience with each choice? No. Async was "chosen" because it was the…
Node wasn't released until 10 years later.
Re: Local async executors and why they should be the default
#116Earlier quoted context omitted.
Go can do this because it’s closer to Java or C# than C. It has a runtime that gets compiled into every binary. Rust deliberately avoided that by design.
Why is runtime needed for a syntactic feature like that? All you need is an async wrapper for system calls, and every sync function could, in principle, be compiled down to a coroutine and ran on an executor of your choice. Just because Go has a runtime and has a go keyword, doesn't in itself mean that runtime is necessary for the go keyword to exist.
You could argue that all go code is async (in the Rust sense), and because of this uniformity, there’s no need for the syntactic distinction that Rust requires you to make.
Rust could have done the same (and at one point in time they did have green threads), Rust decided that the convenience to Rust programmers wasn’t worth the cost elsewhere (the inclusion of a runtime, performance overhead, C interop, etc).
Re: Local async executors and why they should be the default
#117Imagine writing code for a computer game bot: move left, wait for enemy, attack enemy... You normally can't write it like this because it would block the rest of your program. Async allows you to go from "program sequential" to "bot sequential" for lack of better terms. If you are IO bound there is no need for threads. I often like to use one network thread and one GUI thread to keep things separate, and to prevent the occasional blocking in one to cause latency in the other. You just need to have a method to post calls to the other event loop. Works well in Python, as well with Qt-based apps.
C# on the other hand made the same "mistake" of being very general. I think you can even have a coroutine suspend on one thread and wake on another. It looks like you switch threads in the middle of a function.
I guess that is useful when you want to write performant multithreaded servers. I just want to write easy sequential code without worrying about locks or state machines.
Re: Local async executors and why they should be the default
#118Earlier quoted context omitted.
I fully agree that the async story needs attention. Something as drastic as backwards-incompatible changes might not be needed. But definitely much more documentation about best practices, highlighting the option of local async tasks, making the APIs for that more convenient, and also some low hanging fruits in terms of language syntax. It seems a bit like while there was a lot of excitement about the async syntax a…
> now there is a MVP syntax and everything has just slowed down a lot. Sadly this is spot on. Seems like after async was delivered there was only one big push from tokio 0.1 to 0.2 to 1.0 and that was it, the ecosystem and/or language maintainers seem to have lost interest in making it better to read and use.
The reason that progress appears slow is because the next steps for async improvement have required extending the type system, and doing that properly (making sure not to introduce unsoundness) required a massive overhaul to the type checker, and forming an entire new team solely dedicated to the verification of the type system. Here's the most recent update on that work: https://blog.rust-lang.org/inside-rust/2023/07/17/trait-syst...
Re: Local async executors and why they should be the default
#119Earlier quoted context omitted.
> So e.g. you have to use Arc > In terms of C++ code that would equate to std::shared_ptr > which ... sounds quite wasteful in terms of scalability/performance. Why is it not possible to simply return a Rust promise? That's the way I do it in my C++ async (executor) library backed by work-stealing queues under the hood.
I think it's worth understanding why you need Arc and RwLock here. If the compiler fails to type check because a future is not Send, it means that the future's context holds onto some state that is not safe to be sent to a different thread. For example, holding a reference to something on the stack across an await point. If it fails because something is not Sync, it means the future's context holds onto some state th…
Re: Local async executors and why they should be the default
#120Wait, async is multithreaded by default in Rust? For me the whole point of using async in JavaScript or Python (originally with Twisted's @inlineCallbacks) was to get concurrency without threads. Imagine writing code for a computer game bot: move left, wait for enemy, attack enemy... You normally can't write it like this because it would block the rest of your program. Async allows you to go from "program sequential"…
Async in rust is nothing by default because it doesn't have a runtime. You need to use a runtime/executor for async to work, the most popular being Tokio, which can be configured to be single-threaded or multi-threaded.
EDIT: But in a way since Rust make 0 assumption about how async function are going to be executed, it does design around the fact that they might be multi-threaded.