Live data from Hacker News

Why asynchronous Rust doesn't work

eta.st

291–300 of 305 posts

Re: Why asynchronous Rust doesn't work

#291
post #287

Earlier quoted context omitted.

Cargo-geiger and similar tools allow you to audit crates you depend on to discover whether they're definitely safe. Of course just because some Rust is unsafe does not mean it's wrong, it just means that you're relying on it being correct as you have to with all code in unsafe languages.

Even those tools don't validate data corruption, using perfectly safe Rust accessing a table row from multiple threads without being protected from a transaction block or a table row lock. Hence why doing blank statements like Rust prevents data races, without the context when that is actually 100% true, does no favours to the language advocacy.

Data corruption isn't a data race.

I think what you're imagining is just "What if people use Rust to write bad SQL queries?" which again, not a data race. Stupid perhaps, unlikely to give them the results they expected, but not a data race.

Re: Why asynchronous Rust doesn't work

#292

Earlier quoted context omitted.

The original article is just as inflammatory as its top comment if you’ve read it: > In 2017, I said that “asynchronous Rust programming is a disaster and a mess”. In 2021 a lot more of the Rust ecosystem has become asynchronous – such that it might be appropriate to just say that Rust programming is now a disaster and a mess. As someone who used to really love Rust, this makes me quite sad. I imagine withoutboats re…

I'm less comfortable projecting my own opinions into the head of 'withoutboats than you are. The author of the comment they responded to and the author of this post are not the same person.

[deleted]

Re: Why asynchronous Rust doesn't work

#293
post #287

Earlier quoted context omitted.

Even those tools don't validate data corruption, using perfectly safe Rust accessing a table row from multiple threads without being protected from a transaction block or a table row lock. Hence why doing blank statements like Rust prevents data races, without the context when that is actually 100% true, does no favours to the language advocacy.

Data corruption isn't a data race. I think what you're imagining is just "What if people use Rust to write bad SQL queries?" which again, not a data race. Stupid perhaps, unlikely to give them the results they expected, but not a data race.

I am thinking that data races in the scenario of multiple threads accessing a global variable is sold too often, and everything else gets ignored.

While a relevant progress versus what other systems languages are capable of, it still leaves too much out of the table, that tends to be ignored when discussing data consistency safety.

Stuff that usually requires formal methods or TLA+ approaches to guarantee everything goes as smooth as possible.

Re: Why asynchronous Rust doesn't work

#294
post #122

Earlier quoted context omitted.

Rust has stopped depending on closures for async when it moved from experimental futures library to built-in async/await. Rust now easily supports mutable and temporary references in async blocks and across await points. Rust still doesn't support use-after-free, so you can't reference a temporary object and use it in another thread that will outlive it :) In TFA the second do_work_and_then() example doesn't compile,…

You can't do that using anything here? https://doc.rust-lang.org/std/mem/index.html Genuine question, I don't mess around with memory typically.

mem::forget() is not crashy like free(). It merely prevents destructors from being run (e.g. you could do the same by storing the value in a global variable). It's a safe method, subject to usual safety checks.

You can use an `unsafe {}` block to call literally libc::free(), or dereference a random raw C pointer, or do something else crashy. However, when talking about Rust's safety rules it's usually assumed we're not talking about code bypassing these rules on purpose.

Re: Why asynchronous Rust doesn't work

#295

"I’d like to make a simple function that does some work in the background, and lets us know when it’s done by running another function with the results of said background work" This is why he's got a problem, in a nutshell. I suspect the author has a heavy Javascript background and is used to Continuation Passing Style (CPS)[1]. The problem is that in a non-interpreted language that is a horrible way to do things bec…

Very helpful answer, thanks!

Re: Why asynchronous Rust doesn't work

#296

Earlier quoted context omitted.

If you are going to wrap everything in Rc, just use kotlin or c-sharp or python?

Correct me if I'm wrong, but: - GC pauses, Rc does not, it's simply a deallocation by the last reference holder - Rc still has predictable memory allocation/deallocation, GC is not guaranteed to run until needed - More efficient memory use, no need to keep track of allocated objects - The rest of the Rust language is a pleasure to use (imo) - Python is slow for certain applications, and not concurrent - Kotlin is kin…

> More efficient memory use, no need to keep track of allocated objects

RC uses a traditional malloc/free-style heap, which requires bookkeeping for allocations. It also allocates an _additional_ word to track the reference count, which leads to poor cache behaviour (even in single-threaded programs, but especially in multi-threaded programs). Bumping/copying nursery incurs no bookkeeping overhead.

Re: Why asynchronous Rust doesn't work

#297
post #293

Earlier quoted context omitted.

Data corruption isn't a data race. I think what you're imagining is just "What if people use Rust to write bad SQL queries?" which again, not a data race. Stupid perhaps, unlikely to give them the results they expected, but not a data race.

I am thinking that data races in the scenario of multiple threads accessing a global variable is sold too often, and everything else gets ignored. While a relevant progress versus what other systems languages are capable of, it still leaves too much out of the table, that tends to be ignored when discussing data consistency safety. Stuff that usually requires formal methods or TLA+ approaches to guarantee everything…

The context here, right up at the top of the thread where perhaps you've forgotten it, is that (safe) Rust gets you Sequential Consistency, since it has Data Race Freedom.

This makes debugging easier, in the important sense that humans don't seem to be equipped to debug non-trivial programs at all unless they exhibit Sequential Consistency. It's easy enough to write a program for modern computers which doesn't have Sequential Consistency, but it hurts your head too much to debug it.

With your C++ hat on, this might seem like a distinction that doesn't make a difference, lack of Data Race Freedom in a C++ program results in Undefined Behaviour, but so does a buffer overrun, null pointer dereference, signed overflow, and so many other trivial mistakes. So many that as I understand it an entire C++ sub-committee is trying to enumerate them. Thus for a C++ programmer of course any mistake can cause mysterious impossible-to-debug problems so Data Race Freedom doesn't seem important.

Try your Java hat. In Java data races can happen but they don't cause Undefined Behaviour. Write a Java program with a data race. It's hard to reason about what it's doing! It can seem as though some variables take on inexplicable values, or program control flow isn't what you wrote. If you introduce such a race into a complex system you should see that it would be impractical to debug it. Most likely you'd just add mitigations and go home. This is loss of Sequential Consistency in its tamest form, and this is what safe Rust promises to avert.

Re: Why asynchronous Rust doesn't work

#298

Earlier quoted context omitted.

I think you can implement fibers and continuations in WASM by converting the whole program into a giant switch statement and heap allocating frames. There are a few scheme-to-C compilers that do that I think. Mind, it is not going to be fast as it is going to be hard to generate efficient code ...

Emscripten actually has an "ASYNCIFY" feature which does exactly that but (AFAIK) down on the WASM level. It also has surprisingly little performance overhead.

Yes, this is pretty much what I had in mind it seems. From a quick read it is not 100% clear whether it supports stackfull coroutines, but as it is designed to support existing blocking C/C++ code, very likely it does.

Re: Why asynchronous Rust doesn't work

#299
post #193

Earlier quoted context omitted.

Java (and other JVM languages), as well as C# to a certain extent address the points you raise. Secondly, it's not true that golang has a minimal learning curve (I've seen senior engineers write bad golang code when they're onboarded - it takes time to learn the golang way of doing things and its quirks), and it's not even a fundamental goal to have.

I disagree. Firstly, it’s widely agreed upon that Go has quite a lot lower learning curve than just about any other language. Your senior engineer anecdotes sound like outliers. With respect to Java and C#, I addressed a similar question here: https://news.ycombinator.com/item?id=29181361 Even where those languages have nominally improved, it is often so difficult in practice that virtually no one bothers to use the…

Again, having a lower learning curve is a non-goal for any serious project. All languages have their quirks and approaches to expressing concepts, and golang is no different.

Java already has a low latency GC (ZGC), and with GraalVM being more and more used, more frameworks are starting to allow building native binaries (e.g. quarkus.io and micronaut.io and helidon.io). Once Spring gets on board, then a huge part of the ecosystem will.

Secondly, if the only concern is packaging and deploying a single file, that's been possible for a long time now (uber jars), and made easier recently with jlink and jpackage.

All "serious" golang projects I saw use some sort of testing suite (like testify), because the built in testing library is so verbose it's basically useless for anything but simple use cases.

And a built in HTTP sever is not a big deal. It's literally a one line maven import, and many such options exist now.

My employer is a big user of golang, and for all company projects, we have to use a framework they built to write code (basically Spring or ASP.NET DI reinvented (but poorly), plus handling some of golang's bad design decisions around error handling and propagation - but it's far from perfect there's only so much they could do). Not to mention having to use bazel to build them and resolve dependencies (and they say golang compiles quickly, lol). All serious projects will end up in that state sooner or later, and in Java land, it's all available from the start and extremely mature and battle tested.

You're right that the experiences are leagues apart, but that's in favor of the JVM. golang has nothing close when it comes to monitoring and observability and continuous profiling. Nor the tunability of the JVM to select the best GC based on the application type.

Re: Why asynchronous Rust doesn't work

#300

Earlier quoted context omitted.

> intermittently doesn’t work in weird and impossible to debug ways This is my major reason for using Rust. It's far better to beat your head against a wall when you're writing than when you're debugging. In both c++ and c# it's possible to write subtly wrong code that is basically undebuggable. Often these are intermittent things that show up once every million or more runs. There's no amount of time that will satis…

If you are very zealous and use lots of C++17/20 magic, you can prevent lots of runtime bugs with C++ too by doing basically the same stuff you do in Rust (RAII, move semantics, use concepts, etc). Sadly that's not doable in C#.

Move in particular is painful in C++ because to make it work C++ needs to invent these "hollowed out" objects that would be safe to deallocate after there's no longer a real value inside them. Rust doesn't need to do that.

Suppose you've got a local String variable A. You will move A into a data structure which is going to live much longer and then your local function exits.

In C++ when the function exits, A will get destroyed, so when A is moved into the data structure, A needs to be hollowed out so that whatever is left (a String object with no heap storage) can be safely destroyed on function exit.

In Rust the compiler knows you moved A, therefore there is nothing to destroy at the end of the function, no work needed (at runtime).

Post reply on HN