> Shared data is owned by a mutex, and you must borrow that mutex to access the data.
This is how sane locking code works in C or C++.
The critical issue is that we have a lot of code already written which doesn't respect this simple rule.
90% of the time, that C/C++ code needs to be re-architected to make the locks either more comprehensive (fix races) or less (performance).
Rewriting in Rust generally achieves that, because it is a re-architecting and refactoring step with concurrency in mind.
And if done neatly, this leaves no room for the next guy to come in and undo parts of that design, because there's violating that takes more work than keeping it - the wrong approach is suddenly the harder one to get to, unlike C/C++.
The fundamental problem with this pattern in C++ is that you can always just keep around a pointer into it and then the "mutex guard" thing falls apart. There's no way to communicate to the language that this pointer can't hang around any longer than the mutex guard (because that's the only thing preventing the pointer from being racy).
You basically can't write safe abstractions in C++ in the face of pointers.
That's true. Which is why object membership is private by default in C++. You can't get the address of an object member unless the author expressly allows you. That's why I think the GP's complaints about pointers making safe shared data nigh-impossible in C++ was maybe overstated. The language/compiler has things to help you. Much more than C, anyway.
Yes, for big things it's a drag. Even for small things it's verbose to use and awful boilerplate to write. And I totally agree with the spirit of your argument that pointers and pointer aliasing makes concurrency harder in C/C++. Was just pointing out the standard well-known idiom for avoiding the problem. Indeed C++'s 'private' language feature allows the compiler to help you with it.
This is how sane locking code works in C or C++.
The critical issue is that we have a lot of code already written which doesn't respect this simple rule.
90% of the time, that C/C++ code needs to be re-architected to make the locks either more comprehensive (fix races) or less (performance).
Rewriting in Rust generally achieves that, because it is a re-architecting and refactoring step with concurrency in mind.
And if done neatly, this leaves no room for the next guy to come in and undo parts of that design, because there's violating that takes more work than keeping it - the wrong approach is suddenly the harder one to get to, unlike C/C++.