It's hard to see features through the programming language theory jargon, but solid theoretical foundations have worked well for Rust so far.

Jargon terms like "sum types" or "affine types" may seem complicated, but when you see it's actually "enums with data fields", it makes so much sense, and prevents plenty of state-related bugs.

Proposed "effects" mean that when you're writing an iterator or a stream, and need to handle error or await somewhere in the chain, you won't suddenly have a puzzle how to replace all of the functions in the entire chain and your call stack with their async or fallible equivalents.

"linear types" means that Rust will be able to have more control over destruction and lifetime of objects beyond sync call stack, so the tokio::spawn() (the "Rust async sucks" function) won't have to be complaining endlessly about lifetimes whenever you use a local variable.

I can't vouch for the specifics of the proposed features (they have tricky to design details), but it's not simply Rust getting more complex, but rather Rust trying to solve and simplify more problems, with robust and generalizable language features, rather than ad-hoc special cases. When it works it makes the language more uniform overall and gives a lot of bang for the buck in terms of complexity vs problems solved.

What worked for rust is having enums, sane-ish error handling, having sane integer types and the borrow checker, good tooling. The rest is just not that useful compared to how much garbage it creates

You didn't mention parametric polymorphism, which is incredibly useful and important to the language. I'm guessing you intentionally excluded async, but to describe it as "not that useful" would just be wrong, there is a large class of programs that can be expressed very simply using async rust but would be very complicated to express in sync rust (assuming equivalent performance).

Yes rust async isn’t good imo. You can do basically the same thing with stackfull coroutines.

Also the ecosystem is setup so you have to use tokio and everything has to be an Arc.

> You can do basically the same thing with stackfull coroutines.

...Minus the various tradeoffs that made stackful coroutines a nonstarter for Rust's priorities. For example, Rust wanted:

- Tight control over memory use (no required heap allocation, so segmented stacks are out)

- No runtime (so no stack copying and/or pointer rewriting)

- Transparent/zero-cost interop over C FFI (i.e., no need to copy a coroutine stack to something C-compatible when calling out to FFI)

"Tight control over memory use" sounds wrong considering every single allocation in rust is done through the global allocator. And pretty much everything in rust async is put into an Arc.

I don't understand what kind of use case they were optimizing for when they designed this system. Don't think they were optimizing only for embedded or similar applications where they don't use a runtime at all.

Using stackfull coroutines, having a trait in std for runtimes and passing that trait around into async functions would be much better in my opinion instead of having the compiler transform entire functions and having more and more and more complexity layered on top of it solve the complexities that this decision created.

> "Tight control over memory use" sounds wrong considering every single allocation in rust is done through the global allocator.

In the case of Rust's async design, the answer is that that simply isn't a problem when your design was intentionally chosen to not require allocation in the first place.

> And pretty much everything in rust async is put into an Arc.

IIRC that's more a tokio thing than a Rust async thing in general. Parts of the ecosystem that use a different runtime (e.g., IIRC embassy in embedded) don't face the same requirements.

I think it would be nice if there were less reliance on specific executors in general, though.

> Don't think they were optimizing only for embedded or similar applications where they don't use a runtime at all.

I would say less that the Rust devs were optimizing for such a use case and more that they didn't want to preclude such a use case.

> having a trait in std for runtimes and passing that trait around into async functions

Yes, the lack of some way to abstract over/otherwise avoid locking oneself into specific runtimes is a known pain point that seems to be progressing at a frustratingly slow rate.

I could have sworn that that was supposed to be one of the improvements to be worked on after the initial MVP landed in the 2018 edition, but I can't seem to find a supporting blog post so I'm not sure I'm getting this confused with the myriad other sharp edges Rust's sync design has.

You can do rust async by moving instead of sharing data, for example

Generics are incredibly overused in rust like most other features. I’m pretty sure most programs don’t need generics outside of use cases like HashMap