Earlier quoted context omitted.
Rust is a frustrating language for me too. I never saw the need for it, really. I just stuck with the language constructs in C++ that makes it basically impossible for memory leaks/use after free/use outside of bounds to occur or if they do occur, explode loudly in debug environments. I haven't used new/delete in a personal project in like a decade; longer than Rust has been around. Now I'm working on a project where…
I am yet to see someone who can't make memory safety mistakes in C++. You might want to start a tutorial series on how to do that. If this skill can be taught that is, and isn't genetic.
At my day job, I work on a project that's 75% C++ and 25% C#. In the code that we've shipped, when there's a crash in code that I've written, (as opposed to business logic bugs) it's usually a memory safety mistake in C#. There was an interesting architectural choice written a decade before I joined the company where most classes have a synchronous constructor, then an asynchronous initializer, then an asynchronous uninitializer, then a destructor that gets run by the GC. There's no end to bugs relating to crashes because the initializer hasn't been run yet or the uninitializer has already been run.
When I get C++ bugs put across my desk in code that we've shipped to customers, it's usually a bug somewhere else. For instance, the last crash dump that we've gotten from customers in C++ is because a we called a Windows UTF-8/16 conversion function. The Windows function itself is all raw pointer nonsense, so we put a pretty wrapper around it so you put a std::string_view in and get a std::wstring out, or you put a std::wstring_view in and get a std::string out. Well, it turns out Windows is a fucking dogshit operating system. If you initialize a thread "wrong", ie, by calling the C11 function thrd_create, and your locale is set to a CJK language, it will ignore you when you tell it the code page of the multibyte string is UTF-8 and will assume it's the system locale's code page, and will call abort() when it hits a UTF-8 sequence. (instead of perhaps returning an error or null pointer) Those are the sorts of C++ crash bugs that I deal with.
My 'secret' to dealing with memory in C++ is to not deal with it. Make the STL do everything. Make RAII do everything. Can this object be POD with constexpr accessors? Do that. Can these methods be const? Do that. Can these objects live in a STL/boost container? Do that. Do they need to live in a unique_ptr/shared_ptr instead? Fine I guess but it would really be better off as a value instead. Does this class need to have a special destructor/copy constructor/move constructor? Find some other way to do it. If you must, try to find some other way to do it anyway. If you must, spend like 10x as much time scrutinizing it, the way a Rust programmer would do with unsafe. If thing must have special destructor/copy/move constructors, factor out all of the things that need special consideration into a class with the barest minimum. If it must have a special destructor, explicitly delete the copy/move constructors/operators if you can. Avoid indexing into arrays; use range based for loops, or stuffs like transform, reduce, or transform_reduce. The solution isn't to use vector::at() (which does bounds checks) instead of vector::operator[] (which doesn't) the solution is to not use indexes at all.