'&' and '&mut', however, are your 'ref readonly' and 'ref' respectively.
With traditional mutex APIs it's just far too easy to get it wrong. I think you just have to structure your thread-related APIs to be misuse resistant. As humans we're just not good enough at not making mistakes.
The premise of this stays. C# approaches this in a more traditional way, with exposing the set of synchronization primitives. It's a step above C and, usually, C++ still because you don't need to e.g. have an atomic reference counting for objects shared by multiple threads.
Concurrent access itself can be protected as easily as doing
lock (obj) {
// critical section
}
This, together with thread-safe containers provided by standard library (ConcurrentDictionary, ConcurrentStack, etc.) is usually more than enough.What Rust offers in comparison is strong guarantee for complex scenarios, where you usually have to be much more hands-on. In C#, you can author types which e.g. provide "access lease", that look like 'using var scope = service.EnterScope(); ...`, where using turns into a try-finally block, where finally that calls .Dispose() on the scope is guaranteed to be executed.
It's a big topic, so if you have a specific scenario in mind - let me know.