Live data from Hacker News

Why asynchronous Rust doesn't work

theta.eu.org

401–410 of 499 posts

Re: Why asynchronous Rust doesn't work

#401

I'm not totally sure what the author is asking for, apart from refcounting and heap allocations that happen behind your back. In my experience async Rust is heavily characterised by tasks (Futures) which own their data. They have to - when you spawn it, you're offloading ownership of its state to an executor that will keep it alive for some period of time that is out of the spawning code's control. That means all dat…

I'm a giant Rust fanboy and have been since about 2016. So, for context, this was literally before Futures existed in Rust. But, I only work on Rust code sporadically, so I definitely feel the pros and cons of it when I switch back and forth to/from Rust and other languages. The problem, IMO, isn't about allocations or ownership. In fact, I think that a lot of the complaints about async Rust aren't even about async R…

> The article brings up the legitimate awkwardness of passing functions/closures around in Rust.

That's a hard problem when you have linear/affine types! Closures don't work so neatly as in Haskell; currying has to be different.

Re: Why asynchronous Rust doesn't work

#402
post #59

Earlier quoted context omitted.

I am guessing you are a point D/D' in the diagram [1] Although the diagram is for the Rust overall, I think something similar is true for async Rust. All I would say is hang in there. It gets better (from my experience). Rust is not something you can code along as you think. There is a bit a of upfront thinking what the data-structures & data-flow look like. At present, I can know in advance without writing code whet…

> Rust is not something you can code along as you think. This is my biggest gripe with Rust. I want to write code that produces the right result first and make it safe later.

If you're interested in continuing with Rust, I'd suggest that you think about making code that produces the right result first and make it fast later: use `clone` everywhere you run into borrowing problems, use `Rc` or `Arc` instead of references everywhere you run into ownership problems, make the single threaded version work before adding concurrency, etc.

Re: Why asynchronous Rust doesn't work

#403
post #286

Earlier quoted context omitted.

> people also thought garbage collection was impossible in a systems language until Rust solved it No, they didn't. Linear typing for systems languages had already been done in ats, cyclone, and clean, the latter two of which were a major inspiration for rust. Venturing further into gc territory: long before rust was even a twinkle in graydon hoare's eye, smart pointers were happening in c++, and apple was experiment…

Perhaps more accurate to say "safe reclamation of dynamic allocations without GC was not known to be possible in a practical programming language, before Rust". The problem with languages like ATS and Cyclone is that you need heavy usage in real-world applications to prove that your approach is actually usable by developers at scale. Rust achieved that first.

I have always thought Pascal solved that in practise with automated reference counting on arrays

A good optimizer could then have removed the counting on non-escaping local variables

Re: Why asynchronous Rust doesn't work

#404
post #159
post #146

Earlier quoted context omitted.

Only if no other crate in your executable uses tokio or another executor, right? Mixing executors is approximately fatal, AFAICT.

I think that's too strong. You could have two executors with no problem. What might cause issues is if code running on one executor needed close interaction with the other - such as running a task on the other executor. Most of the time this is just avoided by using only one executor. But that's not the only way.

That's a good clarification. What matters is if tasks "running on" one executor end up spawning / blocking on a task "on another executor". You can definitely have "side by side, who cares". The danger though is that you're just using some library, and if it suddenly assumes it can do tokio::task::block_in_place but you were using some other executor you get "Hey! No runtime!".

Re: Why asynchronous Rust doesn't work

#405
post #2

I'm coming around to the position that pure "async" is OK, and pure threading is OK, and green threads (as in Go goroutines) are OK, but having more than one of those in a language is not OK. They do not get along well.

The problem with green threads is that, unless they're a part of the platform ABI, they don't really get along with anything else - including green threads in other languages/frameworks! This makes cross-language/runtime interop unnecessarily difficult, and as I understand, it's partly why Go apps tend to be "pure Go", and even their stdlib insists on using syscalls on platforms where you're supposed to go via libc (…

This makes cross-language/runtime interop unnecessarily difficult, and as I understand, it's partly why Go apps tend to be "pure Go".

Yes. Fortunately Go has enough well-exercised standard libraries that this usually isn't a big problem.

Re: Why asynchronous Rust doesn't work

#406

> projects that depend on like 3 different versions of tokio and futures I ended up here recently. This part is truly awful. I can forgive a lot, and much of what appears in this blog post falls under than umbrella, but the tokio/std futures schism is offensively discouraging. I strongly suspect it is a symptom of some great dysfunction.

I abandoned some projects due to that as well. That, and constant breaking changes in major libraries such as `hyper`. Maintaining Rust code is expensive. More than completing the first iteration of a project. Language such as Go or C are boring, but don’t have that issue.

You can choose to not upgrade your crates to avoid this cost.

Re: Why asynchronous Rust doesn't work

#407

Earlier quoted context omitted.

This post is completely and totally wrong. At least you got to ruin my day, I hope that's a consolation prize for you. There is NO meaningful connection between the completion vs polling futures model and the epoll vs io-uring IO models. comex's comments regarding this fact are mostly accurate. The polling model that Rust chose is the only approach that has been able to achieve single allocation state machines in Rus…

Please, calm down. I do appreciate your work on Rust, but people do make mistakes and I strongly belive that in the long term the async stabilization was one of them. It's debatable whether async was essential or not for Rust, I agree it gave Rust a noticeable boost in popularity, but personally I don't think it was worth the long term cost. I do not intend to change your opinion, but I will keep mine and reserve the…

> Please, calm down.

By the way, that will almost certainly be taken in a bad way. It's never a good idea to start a comment with something like "chill" or "calm down", as it feels incredibly dismissive.

> I do appreciate your work on Rust, but

There's a saying that anything before a "but" is meaningless.

This is not meant to critique the rest of the comment, just point out a couple parts that don't help in defusing the tense situation.

Re: Why asynchronous Rust doesn't work

#408

> Was spinning up a bunch of OS threads not an acceptable solution for the majority of situations? It's worth remembering that this is still a very acceptable thing to do in some cases. And spawning OS threads is sometimes easier to write and easier to read, and easier to reason about than async code. Just because async is in the language, don't feel like you need to use it everywhere.

> Just because async is in the language, don't feel like you need to use it everywhere. Except that you will have to because no alternative library exist

What libraries are you trying to use for what tasks that you can only find async libraries?

Re: Why asynchronous Rust doesn't work

#409
post #249
post #6

As I read through the database example, I saw that the compiler just caught a multi-threading bug for the author, and instead of being thankful, he’s complaining that Rust is bad. I think he should use a higher level framework, or wait a few years for them to mature, and use a garbage collected language until then.

What is the bug? I don't see it.

The bug is that the closure is mutating the same data structure (a Vec in this case) in a different thread - i.e., a data race.

In this specific case, only the spawned thread is mutating the Vec, but the Rust compiler is usually conservative, so it marked this as a bug.

The actual bug is one or both of the following:

1. One of the threads could cause the underlying Vec to reallocate/resize while other threads are accessing it.

2. One of the threads could drop (free) the Vec while other threads are using it.

In Rust, only one thread can “own” a data structure. This is enforced through the Send trait (edit: this is probably wrong, will defer to a Rust expert here).

In addition, you cannot share a mutable reference (pointer) to the same data across threads without synchronization. This is enforced through the Sync trait.

There are two common solutions here:

1. Clone the Vec and pass that to the thread. In other words, each thread gets its own copy of the data.

2. Wrap the Vec in a Mutex and a Arc - your type becomes a Arc>>. You can then clone() the data and pass it to the new thread. Under the hood, this maps to an atomic increment instead of a deep clone of the underlying data like in (1).

The Mutex implements the Sync trait, which allows multiple threads to mutate the data. The Arc (atomic ref count) allows the compiler to guarantee that the Vec is dropped (freed) exactly once.

Re: Why asynchronous Rust doesn't work

#410

Earlier quoted context omitted.

The whole point of async is entirely ergonomics. Anything you can do in async you can do in continuation-passing style. I believe the point the author is making is that they added a feature for ergonomics' sake that ended up not being ergonomic; async-style programming usually coincides with first-class functions and closures, and those are painful in Rust.

> async-style programming usually coincides with first-class functions and closures, and those are painful in Rust. Are they, though? The example the author gives in the article tries to write a multithreaded program as if it is a singlethreaded one. Of course that is going to be painful, whether you're trying to use futures/promises or you're using callbacks. If you want to use multiple threads safely (i.e. at all)…

It is my understanding that the entire point of the async/await sugar in any language is to explicitly take away the appearance of multithreading/concurrency/parallelism in code. I often see the argument for async/await syntax as being made in the following way, it is not natural or feasible to reason about concurrent and multithreaded code, programmers need to be able to reason about all code as though it were a linear list of instructions no matter what is going on with the implementation, therefore, we need the async/await syntax to enable linear, straight line code appearance for programmer benefit. While I do not agree that doing so is at all a good thing, it is the most prevelant argument I have seen in justifying async/await style sugar. If I am right and this is the primary motivation for adding this to a language, your comment is in direct opposition to the entire point of such constructs. Using this reasoning, I think the author was doing as suggested and writing multithreaded code as though it was just a non threaded imperative program.

Note: I don’t really disagree with your view, but it seems that view is in the minority.

Post reply on HN