I just converted a relatively simple node.js program to Rust (because the native libusb bindings for node.js crash immediately on Windows 7 64-bit)
Rust made me worry about a lot more than I wanted to. Threads, locking, mutability. What was 80 lines of:
1. open USB device, get handle
2. start a WebSocket server, accept clients
3. proxy incoming USB device traffic to all WebSocket clients
4. proxy incoming WebSocket traffic to USB device
in node.js turned into battles with dyn FnMut traits to pass "callback/event" handlers around, spawning threads to not block on libusb reading / WebSocket client reading, borrowing, cloning, mutex locking, reference counting with Rust like you can in JavaScript.
You can't just "move variables from one scope into callback/lambda" scope with any sort of ease in Rust.
It sounds a bit like you tried to write Rust like you do JS.
Rust is “move” by default. Passing ownership of a variable to callbacks requires making sure you have the correct types along the way, especially if you want to mutate. It can be easier in many cases to only deal with “owned” data in Rust at first. Clone a lot, and so on.
I have a fair bit of Rust experience, and I agree that it adds a lot of cognitive overhead. It gets better over time as you learn how to appease the compiler, but the borrow checker can still be a pain in the ass. It's something you're constantly aware of, taking up space in your brain.
I can understand the move from C or C++ to Rust, the safety guarantees can be compelling. And it does feel like a much more modern language. However, if something with GC like C# can get the job done, I'd choose that every time over Rust.
Yeah part of this is definitely true - Rust is more more verbose about things you often don't really care about (e.g. `String`/`str` in code that only runs once - who cares?).
But a lot of the extra effort in writing Rust code is about moving runtime errors to compile time. It's like static types - more effort writing it but you don't spend ages debugging typos at runtime. I've had way more instances of spending several hours writing complex code and having it work first time in Rust than in any other language. In C++ it's so rare it makes me extremely suspicious when it happens (did I actually recompile?), whereas in Rust I'm almost starting to expect it.
Modern C++ has a strict type system if you don't walk around it.
Nevertheless, the borrow checker can help with bugs that otherwise could be hiding, which is the actual advantage over C++. But those are not the kind you see right away after running your program again.
I'm not just talking about the borrow checker though - Rust has a much more strict type system in general. It's really explicit about everything, for example you can't just `println!("{}", some_path)` because `Path` might contain invalid UTF-8 that can't be converted to a string.
As another JS -> Rust dev I do agree. Over time I have grown to understand why it complains when I do X, Y and Z and I like having deeper visibility into what's going on. That said, I did some experimentation with Nim and it truly feels like a language that does the best of both. But the community isn't so huge.
Rust made me worry about a lot more than I wanted to. Threads, locking, mutability. What was 80 lines of:
1. open USB device, get handle
2. start a WebSocket server, accept clients
3. proxy incoming USB device traffic to all WebSocket clients
4. proxy incoming WebSocket traffic to USB device
in node.js turned into battles with dyn FnMut traits to pass "callback/event" handlers around, spawning threads to not block on libusb reading / WebSocket client reading, borrowing, cloning, mutex locking, reference counting with Rust like you can in JavaScript.
You can't just "move variables from one scope into callback/lambda" scope with any sort of ease in Rust.