I am the person who hates C/C++ and likes languages like Ruby. I like Rust specifically for the reason that I don't have to learn C++ like "grown ups" and can write my crypto libraries in something much more ergonomic.
When I started writing in Rust, it was 2018 and there was old borrow checker (NLL was experimental) and error messages were much more annoying than today. And still, that was nice language that is easy to write and read once you understand a couple of basic rules.
Here are the rules that you need to learn and always keep in mind, even if Rust had a more verbose syntax. And I bet many people struggling with Rust simply were not lucky enough to have those rules explained to them.
Rule 1. By default, in Rust things are "owned" and "moved", all the time. When you do `x = y`, you literally move "y" to "x". In other popular languages you might expect automatic copying or automatic referencing, but in Rust you get an unusual "move" semantics.
This rule applies universally to all things: be it simple numbers or structs, or references, or weird Rc<T> things or whatever. Once you move "x" to another location, you cannot use it any longer from the old location.
Rule 2. As an optimization, some simple types are marked "Copy" which means that you get a cheap implicit copy whenever you want to continue to use moved value. This applies mostly to numbers and fixed-sized types. This liberates you from explicitly calling ".clone()" on stupid integers.
Rule 3. There are references, mutable and immutable, that let you avoid moving value when you don't need to. At any point of time, each value may have N immutable references or 1 mutable one.
There are lexical references (most commonly used): & and &mut, for these the rule 3 is statically checked at compile-time. And there are runtime counterparts that implement rule 3 in runtime: Cell/RefCell.
So when you design an API or use an API, you should really think about ownership of the values that you pass in according to these 3 rules. You may have these questions and use the rules above to answer them.
- Should that API "consume" (get the value "moved into") that value, or should it have read-only or mutable access to it?
- What part of code (which other value) actually owns the value and is responsible for cleaning it up?
- Is that a dumb value that can be Copy-ied explicitly, or is there non-trivial copying cost that requires explicit references or clones for it?
- What does it mean to say `y = &x`? Per rule 1 we move reference "&x" to "y", but since it's a non-mutable reference, we can have a lot of them, so it implements Copy and &x can be used again per rule 2.