To be fair withoutboats didn't really address any of the points that people make about the flaws of async Rust (e.g. the cancellation problem).
It may be the case that there's no good way to solve them, but that doesn't mean that they aren't problems and it doesn't really help to say that people who highlight them are totally wrong and don't know what they're talking about.
That said I think your second link does a much better job of explaining the issues than this post does.
withoutboats has explored these trade-offs on their blog, e.g. https://without.boats/blog/poll-drop/ and as part of io-uring investigation. io-uring is the main use-case for async drop, but even that has been mostly solved without language changes: there is tokio-uring now.
Apart from requiring a safe zero-copy io-uring abstraction to use a buffer pool instead of "bare" references, Rust's polling+cancellation model is fantastic. It's incredibly convenient to be able to externally time out and abort any Future, without needing to pass contexts/tokens and/or needing every I/O operation to have an explicit timeout option.
It's annoying when conscious small trade-offs are framed as fatal flaws (like the title of TFA). In the case of the cancellation model the "doesn't work" is that a safe abstraction is not entirely zero-cost. But when you use async in JS, C#, or Golang, you already have unavoidable heap allocations and memcpys (and another set of difficulties with cancellation). Rust's worst-case flaw is comparable to a normal mode of operation in a few other async languages. The scale of the issue is overblown, and its benefits are overlooked.
I’d say both. The author of the post called Rust async/await a disaster, and the top commenter, who withoutboats replied to, agreed with the author’s criticism.
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 read both and was pretty upset.
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.
Oh, thanks for that link. Withoutboats is being quite emotional, but it is understandable to be emotional about their brain child.
I am tempted from time to time to try Rust, but in the end, I don't think it provides enough benefits for me to consider it. I like high-level, and when I am going low-level, I don't want to be hand-cuffed. It seems with Rust, I am paying all the time just for the ability to go into hand-cuffed low-level mode.
I'd say seat belts more than handcuffs.
When going low level it's easy to slip in memory leaks and security vulnerability.
If you care about developer speed and security but don't care about correctness or performance you can rely on someone's else implementation and write higher level code (eg. use node and offload low level security considerations to node).
If you care about developer speed and performance but don't care about correctness or security use a low level unbounded language (eg. write C and triple check your code)
If you care about security, correctness and performance but don't care about developer speed, use Rust.
To be frank I find the point about developer speed in Rust to be a bit exaggerated, it's pretty high level and the tooling is pretty great.
- Compiler speed is a bit hit or miss
- Debugging life cycles definitely take some time, but with time you get the hang of it and you can quickly copy paste previous solutions to the current problem.
- You save a significant amount of developer time by fixing things alerted by the compiler instead of finding errors at runtime
All things considered Rust is definitely the language I feel more productive in (before it used to be Haskell).
Oftentimes considerations about hiring / collaborating with other developers take the precedence over security, correctness and performance.
Right now I'm maintaining a node.js project, 2 Python projects and a Rust project - mainly because node.js and Python worked best with those teams and the Rust one is a solo project.
> To be frank I find the point about developer speed in Rust to be a bit exaggerated, it's pretty high level and the tooling is pretty great.
Rust has a long learning curve that most people won’t make it through. For them, they might feel the language is slow to developenin because they haven’t gotten to the point where they are “thinking” in Rust.
As a Haskeller, you are well suited to learn Rust; due to their similar ML lineage (the original Rust compiler was written in OCaml), Rust and Haskell make some similar design considerations. I’d say one thing Rust does right is take a lot of the things that make Haskell safe and make them fit in an imperative language.
That's an interesting take on Rust. Given that I have written a lot of code in Standard ML, I might give Rust a try after all.
GP's statement "If you care about security, correctness and performance but don't care about developer speed, use Rust." threw me a little off here. Because, Standard ML is very quick and easy to develop in. So should Rust not inherit that property?
Yes, I think Rust does get there. The thing of it is, I think people are measuring "time of development" differently. To some, the time developing a thing is the time spent writing it, and then getting it out the door. They often don't include things like fixing bugs that are discovered down the line and handled maybe by a completely different team. There's a difference between writing something and writing something right.
The Rust compiler and specifically the borrow checker help you write code that works the first time, which is a property Haskell famously exhibits. In order to get there, sometimes an involved conversation with the borrow checker or type checker is needed. What this means is sometimes you might spend a good long while figuring out a particular lifetime and ownership annotation in order to satisfy the borrow rules. Another implication is that sometimes the way you've architected your code is just plain wrong as far as the borrow checker is concerned. Sometimes it's hard to tell when you just need to add a particular annotation to your code to make it work, or if you actually need to rethink everything entirely. For example, if you go writing a doubly linked list in Rust thinking you'll use pointers, that's just the wrong way to go about it in Rust world, so rather than trying to force that style of code, doing things the Rust way (using Rc<RefCell<T>>>) will yield better results.
But once you get your code to compile, things generally work in a sense that the code will never fall victim to a whole class of bugs that frequent code written in other languages (segmentation faults, use after free, memory leaks, dangling pointers, etc.) You just won't see these things in safe, idiomatic Rust code.
And that leads me to developer speed. Once you and the borrow checker are on the same page, it's like night turned to day. Early when I first started writing Rust, I would spend most of my time dealing with borrow checker errors. Now that I know what the borrow checker expects, and I've internalized all the strange syntax and edge cases, the borrow checker fades into the background and I can code in Rust just about as fast as I can in TypeScript.
As a Haskeller, I expect you might approach writing Rust code in a functional style, and I think this would serve you well. The general approach that newbies take in Rust and which finds strong resistance by the Rust compiler is copious use of passing references for mutation, and creating a rat's nest of pointer references. This style of coding doesn't go over well in Rust, and unfortunately a lot of coders practice this because it's permitted by their native language.
Instead, it's better to lean into type checking and actually use the type checker to save you from having to write a lot of code. Take advantage of immutability and referentially transparent function. Make heavy use of pattern matching and Maybe monads cough I mean Option<T>. We don't use that word in Rust. Haskellers get all of this.
I am currently using Swift, and I like it a lot. But you are kind of siloed into the Apple ecosystem. So I am looking for an alternative.
I am thinking TypeScript might give the biggest bang for the buck:
* You have the widest possible reach via web apps & Electron
* You have an eval/Function object for compiling code on the fly
* You can embed low-level code via WebAssembly (and here Rust might be relevant for producing that WebAssembly)
* If you need access to your TypeScript libraries on mobile, you can still do that via embedding a hidden WebView.
Honestly, as a consumer of libraries, I much prefer when they're written in Rust (without gratuitous use of `unsafe`) compared to a language which doesn't make you think about these sorts of things as hard.
For me, writing a program is mostly writing a bunch of libraries that I then put together. So I am consuming my libraries mostly myself. Therefore, for me it is important that both the library writing and the library using can be done efficiently.
I assume that having a Rust library of something is a good thing. The question for me is: How easy and natural and quick is it to express a concept as a library in Rust?
1. Do you like strong type systems? If so, Rust us great! If you really would e itching for some python or JS, it may bother you, even though the Rust type inference really makes it a non-issue.
2. Do you care about allocations? One could be cheeky and rephrase it as "do you care about performance", but caring about allocations is one of the more advanced optimization concerns and GCed languages can have great performance if you dont hit their pathological cases. But the thing about Rust is that you have as much control as possible to avoid allocating. Entire libraries can allocate nothing more than temporary stack variables, with their primary data being lifetime constrained references the user passes in.
2.5 if you dont care about allocations, then does `Rc<Foo>` bother you? There are very easy escape hatches when you _just don't care_ if that thing is shared and don't want to deal with lifetimes. But Rust _lets you choose_.
I see type systems as a necessary evil. Obviously, I like to be able to distinguish a 32 bit integer from a 64 bit integer or a String. And being able to define interfaces is important. But I am not a fan of modelling all sorts of high-level issues as a type system, because that always comes at a prize: When it works, it is great, and otherwise it really really sucks.
So yeah, Rc<Foo> and stuff like that would bother me, because most of the time, these things are rather peripheral to what I am implementing.
I feel that, when performance is really really important, it is more important to be able to generate code on the fly, and I don't think Rust supports that.
But I haven't tried out ownership in Rust. I will have to work with it for a while to see if it bothers me or not.
If you say that "Rust sometimes is guardrails, sometimes it is handcuffs, sometimes it is a rollercoaster", that is already a lot of information. That's exactly what I want to avoid when implementing a concept: I don't want to be dependent on how well the type system fits my concept, because I am dealing with all sorts of concepts.
Ok. Sometimes Rust fits good, sometimes it fits bad. If you don't want to deal with that, that's fine. I don't want to deal with memory leaks and stuff, so I use Rust instead of C/C++ when low-level, and I deal with the bad fits.
I'd still recommend learning Rust though. I find the way (the clean ways, not the ugly ways like Weak/Cell/etc.) it forces you to structure your programs is a very useful pattern when dealing with unrigid code like in C or C++, since its performance is near those languages. Then when coding C/C++ you can just break out of that pattern when you need to (e.g. self-referential structs).
BTW, what languages do you usually work with? I've been eyeing up Zig since it's nearly adjacent to C, but I'm waiting until stabilization.
I am working mostly in Swift these days, and some Metal C for GPU and GPGPU. If Swift and SwiftUI both also ran in the browser, that would meet my needs.
I need to break out of that Apple silo though for an important project of mine, so I am looking for alternatives. I was looking at zig, too! It seems very promising, and comptime seems like a great idea. I am considering both Rust and Zig for generating libraries in WebAssembly.
I think I will mostly go with TypeScript for my project, it seems undogmatic and highly productive, and Turbo Pascal was my second language after Quick Basic, so I like its origins :-)
I am sort of in the same boat. I use C++ for server type applications. I consider modern C++ safe enough. While there are no explicit safety guarantees I am a practical man. My servers work for years without any complaints from clients. No memory leaks, no crashes. That's the end result and I think from that perspective for me using Rust would be ROI negative as I would have to port / replace a lot of code I reuse in my various products.
Things of course would be different should the client insist on implementation done in Rust but so far not a single client of mine ever mentioned using this language.
The point of Rust is that your mediocre C++ developers won't be able to write some kinds of bad code patterns with it, since it won't compile.
The problem is that most likely those mediocre C++ developers won't be able to write any Rust at all.
If you do have good C++ developers, you're also probably better off sticking to well-established and battle-proven C++.
So the use case for Rust remains niche. It's a way to attract the programming language nerd that knows more about Haskell than he does about systems programming. Maybe not exactly the best fit? But worth trying if you want to differentiate yourself from the competition.
And this blog post from someone who did spend a lot of time working with async rust: https://tomaka.medium.com/a-look-back-at-asynchronous-rust-d...