Rust and C++ work very similarly with respect to objects you own. In both languages they get cleaned up by a destructor when they go out of scope. However, Rust also has a lot of language features that deal with objects you borrow, i.e. have a pointer to. In C++ you might make a "use-after-free" mistake and hold a pointer to an object that's already been destructed, which can lead to all sorts of memory corruption. In Rust, the same mistake almost always results in a compile-time error. The part of the compiler that enforces all this is called the "borrow checker", and getting used to the borrow checker's rules is a big part of Rust's learning curve.
One thing C++ programmers might be interested to learn, is that this doesn't only apply to simple variables and references; it also applies to library data structures and their methods. The Rust compiler doesn't really "know" what the .clear() method on a Vec does, but it knows enough to prevent you from calling that method while you're holding references to the Vec's elements.
> getting used to the borrow checker's rules is a big part of Rust's learning curve.
It’s also a big part of C and C++’s learning curves, it’s just that the compiler doesn’t tell you about it; you’re being taught by segfaults and silent memory corruption instead.
Very true. My hot take on this is, if you want to learn C or C++ "properly" (not just well enough to get your homework done, but well enough to write medium-size programs that pass ASan/UBSan/TSan), the fastest way is to learn Rust first. This is emphatically not because Rust is quick to learn, but rather because ownership and borrowing discipline takes years of mentorship to absorb in C and C++, and plenty of career programmers never really get it.
> Rust and C++ work very similarly with respect to objects you own.
There is one major difference. In Rust you can memcpy objects you own to a different base address and they will still work, unless they're pointed at by a Pin<> type (as such, code that requires objects to stay put in memory must take a Pin<> reference. This arrangement may potentially be replaced by a special "?Move" trait in future editions of Rust). C++ pins all objects by default and allows objects to have custom "move" constructors, to sort of enable them to be moved elsewhere.
In C++, objects that get "moved" must leave behind something that can be destructed cleanly; Rust has no such limitation, at least wrt. ordinary moves. Arguably the closest thing it has to a C++ move is the .take() pattern, which leaves a default-constructed object behind. But this is rarely used.
The general tradeoff is that the Rust pattern memcpy's compound objects more often, but has less gratuitous use of the heap and less pointer chasing compared to idiomatic C/C++. This is a valid choice once one has the compiler support that Rust provides.
> However, Rust also has a lot of language features that deal with objects you borrow
An other major safety difference is that Rust uses destructive moves, once a binding is moved-from it becomes invalid / inaccessible (and it won't be destroyed).
In C++, a moved-from object is in a "valid but unspecified state", so it must be destructible (because destructors always run) but any interaction with the object other than destruction may be UB, and the compiler won't tell you (in the same way it won't tell you about borrowing issues whether it's UAF, invalidation, ...).
One thing C++ programmers might be interested to learn, is that this doesn't only apply to simple variables and references; it also applies to library data structures and their methods. The Rust compiler doesn't really "know" what the .clear() method on a Vec does, but it knows enough to prevent you from calling that method while you're holding references to the Vec's elements.