This is completely back to front. Of course you have to internalise the rules of a borrow checker or type system to be highly productive. How can you hope to do a good job without that?
This is completely back to front. Of course you have to internalise the rules of a borrow checker or type system to be highly productive. How can you hope to do a good job without that?
This is generally a good thing: the more you internalise the logic of borrow checking, the earlier you start thinking about "who owns what" instead of deferring the choice to later, which often ends up in a tangled mess of "incidental data structures" as it is sometimes called in the c++ world [1].
Of course in c++ this means you have to internalise this discipline the hard way, i.e. without the borrow checker helping you.
[1] https://isocpp.org/blog/2016/05/cppcon-2015-better-code-data...
My take-away was that the article concludes that Rust is NOT a good tool for working with the borrow checker. I don’t think it said that there’s anything wrong with a borrow checker, and that you shouldn’t learn the basics of how it works or how to write idiomatic Rust.
A good tool shouldn’t require you to have a perfect memory of all the rules for you to be highly productive with it. If you make mistakes it should quickly tell you so with a message that quickly lets you figure out what to change.
I think this stands in contrast with Zig where these goals is the highest priority of the language. It’s also very strict with little to no undefined behaviour. But there’s also a lot of discipline in not introducing syntax or semantics that makes it hard for the compiler/checker to give a quick pass/fail with a clear message about went wrong. You can see from the issues in GitHub that improving error messages for failures in the type system is consistently prioritised. That puts a hard constraint on Zig where they’re held back from putting too much power into the type system.
That’s not to say that Rust doesn’t prioritise being a good tool. But the semantics of borrow checking makes their job an order of magnitude more difficult here. It’s an inherent trade-off. They’ve made a huge jump in the complexity and power of the language, and it’s probably much harder to then make a tool that makes it comfortable to work with this kind of power.
Some here may find it easy to deeply understand all the rules and to write code the first time that doesn’t trip up Rust too much. But in the real world code is written by many different kinds of people with different kinds and levels of intelligence.
I’ve found this to be an important consideration when choosing languages, libraries, tools and methodologies for large teams.
One way to be a 10x programmer is to write 10x as much code as an average programmer. Another way is to make 10 other average programmers twice as efficient, and that’s clearly more scalable.
Rust may still be a good choice to make the team more productive in the long run. My point is that adopting it in a team should perhaps not be considered a trivial decision.
> A good tool shouldn’t require you to have a perfect memory of all the rules for you to be highly productive with it. If you make mistakes it should quickly tell you so with a message that quickly lets you figure out what to change.
That's exactly what the Rust borrow checker does for me.
The borrow checker rules are quite simple conceptually.
If I own a book, I can read it, write in the margins or even destroy it. `let book: Book = Book{...}`.
I can lend this book to you exclusively `&mut Book`, you can read and write it, but not destroy it. And nobody else; including me; can even read it until you are done.
I can lend this book to you and others for reading `&Book`. We can all read it concurrently. And I must wait for everybody to be done before I can regain full ownership.
I can give you the book (passing by value). And it's now yours to do what you please. Including destroying it `drop(Book)`.
Sometimes you do want to share to many; and maybe even gate exclusive write access; at runtime. This is where Rc, Arc, Cell, RefCell and Mutex come in.
Rc and Arc destroy the book when a reference counter drops to zero. Another way to look at it, is that when the counter is 1, you have sole ownership of the book. And you can do with it what you please.
As for the runtime check for mutability, Mutex should be obvious. Cell and RefCell are similar but within a single threaded context.
And finally when you know better than the compiler, you use pointers (instead of references) and triple check your work within `unsafe` blocks.