Live data from Hacker News

Local async executors and why they should be the default

maciej.codes

101–110 of 327 posts

Re: Local async executors and why they should be the default

#101

Just 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…

If you want golang in Rust just use channels and tasks? Or threads?

I don't find async Rust difficult at all, I'm having a hard time really empathizing with this to the extent of needing breaking changes.

To me, async from a lang perspective is virtually done - in 2024 I suspect all of the various impl Trait and async Trait stuff will be done and at that point I don't see anything left.

Re: Local async executors and why they should be the default

#102
post #98

Just 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 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.

Re: Local async executors and why they should be the default

#103
post #97

Earlier quoted context omitted.

One thing I hate about "async" systems in general is the absurdity of suddenly having two types of functions behave differently with same syntax. And you have to add "await" keyword to async functions to make them synchoronous, i.e. behave normally. I find the Go's approach of "everything is synchronous, except when made asynchronous with the 'go' keyword" much more pleasant and much less error prone. Is there a Rust…

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

#104

Just 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…

If you want golang in Rust just use channels and tasks? Or threads? I don't find async Rust difficult at all, I'm having a hard time really empathizing with this to the extent of needing breaking changes. To me, async from a lang perspective is virtually done - in 2024 I suspect all of the various impl Trait and async Trait stuff will be done and at that point I don't see anything left.

I do that of course, and that's one of the easiest ways to use async Rust. In real projects you need much more however. F.ex. I had to code an example of how to add tasks to an already running pool of tasks and posted my findings here: https://github.com/dimitarvp/rust-async-examples/blob/main/e... (there's #2 as well with some more comments and a different approach).

The fact that I needed to make a GitHub repo and start making show-and-tell demos on how to do various things with async Rust to me is both a red flag and me being diligent BUT it should be more obvious. And promoted in docs.

Rust started suffering from "you got all the nuts and bolts in place, now build your own solution, son" syndrome which I grew to dislike. Too low-level. I wouldn't mind something akin to e.g. Golang's flowmatic library (check the first two examples at the top of the README): https://github.com/carlmjohnson/flowmatic

Re: Local async executors and why they should be the default

#105
post #85
post #73

Earlier quoted context omitted.

They're not green threads. Futures are stackless coroutines.

And green threads aren't a panacea. They introduce runtimes, which was something Rust avoided on purpose.

True. Rust's mandate for "zero runtime cost abstractions" is one of the better justifications for async/await. Because async/await, too, has significant limitations, notably:

1. Coloured functions

2. Placing the concurrency design decision (async vs not) on the implementer of a function, not the caller

3. Debugging complexity

(1) and (2) are mutually, negatively, reinforcing. As the implementer of a library function, I can't possibly know all the possible scenarios in which my function will be called. If I make it synchronous, then I'm blocking my caller. Which might be an issue - or might not. If I make it async then I don't block the caller - but I've just consigned the entire call stack to be async.

There's clearly strong opinions on both sides. Some are strong advocates of async/await. Personally, I'm not. Multiple, concurrent, fine-grained sequences of actions fit my mental model much better. Async/await doesn't give me that: I find reading async/await code to be a bit like reading control flow with GOTOs.

My preferences are undoubtedly influenced by working with Erlang, which has supported fine-grained sequences since the beginning. There are no coloured functions; just functions. As the caller, I can decide if I want things to run sequentialy (call from another function) or concurrently (spawn as a separate Erlang process). I have the context I need to make that decision.

It's not a panacea by any means (see e.g. Structured Concurrency [0]). But it fits my mental model much better.

YMMV of course.

--

[0]: https://en.wikipedia.org/wiki/Structured_concurrency

Re: Local async executors and why they should be the default

#106

Earlier quoted context omitted.

If you want golang in Rust just use channels and tasks? Or threads? I don't find async Rust difficult at all, I'm having a hard time really empathizing with this to the extent of needing breaking changes. To me, async from a lang perspective is virtually done - in 2024 I suspect all of the various impl Trait and async Trait stuff will be done and at that point I don't see anything left.

I do that of course, and that's one of the easiest ways to use async Rust. In real projects you need much more however. F.ex. I had to code an example of how to add tasks to an already running pool of tasks and posted my findings here: https://github.com/dimitarvp/rust-async-examples/blob/main/e... (there's #2 as well with some more comments and a different approach). The fact that I needed to make a GitHub repo and…

I'd like to think that I've worked on some "real projects" and I've never run into these issues tbh. It's just hard for me to wrap my mind around. Like I said, I get wanting more libraries (although I think they exist? A local pool exists and you can spawn tasks in it, using tokio) but I'm just not seeing a fundamental problem.

I found the code you wrote very confusing so I can see why, when you come back to it, you find it confusing. But I don't really understand its purpose either.

Re: Local async executors and why they should be the default

#107
post #3

Multithreading, why does everyone always do it wrong? One of life's big questions.

CPU cycles are practically free but memory latency and synchronization are not. (Hardware threads gives more CPU cycles capacity). People's scalability problems are memory related, not CPU cycles. In other words, we can't scale updating a single memory location with more hardware threads. People often want to scale the updating of something so they add threads, but the contention of memory synchronization prevents that.

Scaling independent memory updates works well and is embarrassingly/pleasingly parallel.

I am working on a multithreaded design that is scalable and easier.

The idea is that if you represent your problem, data flow and control flow as a tree where branches do not communicate, you can get the purported scalability of multiple hardware threads.

Re: Local async executors and why they should be the default

#108

Just 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…

If you want golang in Rust just use channels and tasks? Or threads? I don't find async Rust difficult at all, I'm having a hard time really empathizing with this to the extent of needing breaking changes. To me, async from a lang perspective is virtually done - in 2024 I suspect all of the various impl Trait and async Trait stuff will be done and at that point I don't see anything left.

I think this blog post by tomaka gives a good summary of the current issues with async rust. Certainly much more comprehensive than what you can write in a hacker news post:

https://tomaka.medium.com/a-look-back-at-asynchronous-rust-d...

It highlights some of the same problems:

- “Just spawn a new task”

- The Send trait isn’t what it means anymore

Re: Local async executors and why they should be the default

#109
post #98

Earlier 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.

AFAIK the focus right now is on supporting `async` in traits, which required a bunch of language features seemingly unrelated like Generic Associated Types, which were released in rust 1.65, and Type Alias Impl Trait, which is still WIP due to some complications.

Re: Local async executors and why they should be the default

#110
post #98

Earlier 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 fun part is over. At least that is how a rust maintainer described it to me.

When I asked why doing the unfun part (documentation, maintenance, etc.) isn't a condition for allowing people to do the fun part (pushing cool new code), it got no response.

Post reply on HN