Live data from Hacker News

Why asynchronous Rust doesn't work

eta.st

71–80 of 305 posts

Re: Why asynchronous Rust doesn't work

#71
post #12
post #6

In summary asynchronous programming without a GC is a pain in the a*. That's somewhat expected isn't it?

It’s a pain with GC as well, coming from a C# background. It’s incredibly easy to write something that intermittently doesn’t work in weird and impossible to debug ways.

> 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 satisfy you after you've run into this, even after you've fixed the problem. It's like having a stalker, you never know if they've stopped stalking you. You can lock your doors, you can buy an alarm system (eg Valgrind suite). But you won't know whether you've really solved the issue.

Of course it's possible there will be other undebuggable issues with Rust, I'm going more by reputation and general impression in blogspace rather than a thorough study. But I thought it was worth a try, and I've been positively impressed thus far.

Re: Why asynchronous Rust doesn't work

#72

Earlier quoted context omitted.

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

Rust has advantages over C#, even for high level programming: * Rust traits are more flexible than C# interfaces, especially when combined with generics (implementing traits for foreign types, associated types, each method can have its own constraints, conditional trait implementation, #derive) * Rust has much stronger thread safety guarantees (absence of data races, preventing access to a mutex's data without lockin…

I haven't used C#, so I can't comment on the IDEs there.

However, it's surprising to me that you can get a better experience than VScode + rust-analyzer. It feels like magic when I use it.

Re: Why asynchronous Rust doesn't work

#73
post #7

The article glosses over async Rust and is mostly a rant about how closures are difficult in Rust. Most of the difficulty comes from Rust not having a GC yet wishing to keep track of object lifetimes precisely: a GC'ed language needs no distinction between an ordinary function pointer and a closure that captures the environment. But Rust being a low-level systems language chose not to have a GC. Another popular langu…

The article is also conflating synchronous single-threaded, synchronous multi-threaded and asynchronous programming.

Each have their own usage, and no, a multi-threaded program is not the same as an asynchronous one. For example, using threads and channels instead of async/await is not a design flaw if your workload is mostly about large, blocking computations on a read-only shared state with no I/O. In that situation, lifetime and closures will not pose any issue and won't get in the way because you're not mutating anything and communicate with owned messages.

The async ecosystem is evolving and will be better tomorrow than it is today. Saying that Rust programming as a whole is a mess because async/await is harder to use than necessary, is short-sighted. It's like saying you don't like apples, but you only chewed on the branch of the tree.

Re: Why asynchronous Rust doesn't work

#74
There are constant efforts to make async easier in all languages. It'll never be as easy as writing synchronous code.

I really don't like futures/promises, I don't know where this abstraction came from.

Callbacks are where it's at. Someone, somewhere has to write callback code; they cannot be got rid of. What works for me is keeping the callback handler as short as possible, this usually means just pushing 'work' onto an async queue that is serviced by one or more threads.

Re: Why asynchronous Rust doesn't work

#75
post #70

Earlier quoted context omitted.

From my very limited expeirience with Rust I noticed that it becomes way more easy and laid back language when you just skip using references and lifetimes nearly completely and just wrap everything in Rc . Then you are getting expeirience of fairly high level language with a lot of very cool constructs and features like exhaustive pattern matching and value types with a lot of auto-derived functionality. Does Rc hel…

Then you are essentially introducing a verbose, unoptimized garbage collector?

And you still run into all sorts of ergonomic issues when you inevitably need to dereference your Rc pointer. That said, I sympathize with the parent's desire for a proper Rust-lite with a GC: C-family syntax, great tooling, great documentation, great standard library and ecosystem, native+static compilation by default. Of course, someone will ignore those criteria and come in suggesting OCaml/Reason...

Re: Why asynchronous Rust doesn't work

#76

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…

> GC pauses, Rc does not, it's simply a deallocation by the last reference holder

While the last bit is true, it may actually end up having to do lots of work. Think large object graphs where the last reference to any of it goes out of scope.

Also, as a matter of terminology: Rc is a form of garbage collection. It's also a pretty bad general GC strategy at that -- which is why one doesn't just slap an Rc on everything.

Re: Why asynchronous Rust doesn't work

#77
post #7

The article glosses over async Rust and is mostly a rant about how closures are difficult in Rust. Most of the difficulty comes from Rust not having a GC yet wishing to keep track of object lifetimes precisely: a GC'ed language needs no distinction between an ordinary function pointer and a closure that captures the environment. But Rust being a low-level systems language chose not to have a GC. Another popular langu…

I think it's not entirely fair to paint problems with rust's async features as an aversion to low-level-ness.

If anything, I think the issue with Rust's async is that it tries to be too high level.

In my experience with async rust, most of the difficulty comes from "spooky errors at a distance". I.e, you're writing some code which feels completely normal, and then suddenly you are hit with a large, obtuse error message about types and traits you didn't even know you were working with. The reason being, something went wrong with all the inference involved in implicitly wrapping everything in futures. And often it's not even related to the line of code you just wrote.

So in these cases, I think the issue is that rust tries very hard to wrap async in a very smooth, sugared syntax. It works fine in a lot of cases, but due to the precise nature of Rust, there are a cases where it doesn't work. And because the system relies on a lot of magic inference, the programmer has to hold a lot of assumptions and rules in their mind to work effectively with async.

I am not an expert, but from my experience it feels like async rust was a bit rushed. It should have been introduced as a more explicit syntax first, with sugar on top once the guard rails had better been figured out through experimentation.

That is under the assumption that future-based async in rust is a good idea at all. Rust's ownership model becomes intuitive very quickly in the single-threaded case, but at times it feels like a square peg in a round hole where async is concerned.

Re: Why asynchronous Rust doesn't work

#78

Earlier quoted context omitted.

From my very limited expeirience with Rust I noticed that it becomes way more easy and laid back language when you just skip using references and lifetimes nearly completely and just wrap everything in Rc . Then you are getting expeirience of fairly high level language with a lot of very cool constructs and features like exhaustive pattern matching and value types with a lot of auto-derived functionality. Does Rc hel…

`Rc` freezes the value as long as it's shared unless you use interior mutability. It's fine when you want to share immutable data, but if you ever need to mutate its contents you will have to deal with awkward cases and situations, and at runtime! Wrapping everything in `Rc` is not a good default strategy. Instead figure out an architecture that works well with the borrow checker! This normally means thinking hard ab…

> I love Rust precisely because of this. You can leverage the compiler to drive your development and design. If it's hard, then it's probably wrong!

Yes, I had the same experience writing code in Rust and Haskell. If the design works nicely with the language, then also the design is better, more modular, simpler dependencies and easier to understand and extend.

Re: Why asynchronous Rust doesn't work

#79
post #38

Earlier quoted context omitted.

When async is visible in the type system it means you need to write things like iterating containers twice: once for dealing with non-async values, and a second time for handling async operations. Well maybe this isn't the best example because you already have iterators for the plain iteration part; however let's say you wanted to make an iterator for a container that needs async to iterate the data, then you find yo…

> When async is visible in the type system it means you need to write things like iterating containers twice It should be noted that this is only if the type-system in your language is unable to properly abstract over containers and their capabilities. If it is powerful enough however, then you can write the code once, under the (important!) assumption of short-circuiting in the case of errors.

Sure, it's possible to parametrize your algorithms.

However, do you need to explicitly parametrize them or does the language do it for you? I'm not aware of any language that would remove the need to consider them and just automatically "work" with async types whereas "normal" threading—with errors handled via exceptions—more or less work out of the box the way you expect.

Re: Why asynchronous Rust doesn't work

#80

Earlier quoted context omitted.

From my very limited expeirience with Rust I noticed that it becomes way more easy and laid back language when you just skip using references and lifetimes nearly completely and just wrap everything in Rc . Then you are getting expeirience of fairly high level language with a lot of very cool constructs and features like exhaustive pattern matching and value types with a lot of auto-derived functionality. Does Rc hel…

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

While I understand that with Graal and .Net you can ostensibly make native, static binaries for Kotlin or C#, I'm very skeptical that it works well in practice. In particular, I'm guessing it will feel like swimming upstream, fighting an ecosystem and build tooling which mostly assume you're running on a VM. And then there's the question of performance...

And of course, with Python your code will run 100x slower than either of the above, your dependency management will suck, you won't be able to statically compile, and to top it all off everyone will give you recommendations for your ailments which take a long time to try out and inevitably will fail miserably for one glaring reason or another. By way of example: "just rewrite the slow bits in C" -> you rewrite the slow bits in C -> your code is now slower because the marshaling costs exceed the gains + your build system is dramatically more complex and you have the sheer joy of debugging segfaults and undefined behavior (yeah, I know Cython exists).

Post reply on HN