Conversation

Shared ownership / mutability is inherently complicated. It's very difficult to verify as correct, whether you're a compiler or a human. It ventures outside of what Rust can statically verify as correct without runtime overhead like references, Box<T>, Vec<T>, iterators, etc.
1
2
Usually, the rules are all statically checked, such as the lifetime and aliasing rules for references, which compile down to raw pointers without instrumentation. If you want mutability in Rc<T>, that has to be dynamically checked, which is what Rc<RefCell<T>> provides.
1
So instead of &x, you get a reference with cell.borrow(), and a separate method for mutable references. It provides dynamic checking for the rule of preventing aliasing mutable references. If you avoid shared ownership, you avoid needing more verbose code and runtime checks.
2
1
Mutex<T> is basically the same thing, but the methods obtains the lock for you, and release it when the returned object goes out of scope. The standard lifetime checks prevent using reference derived from it after the RefCell/Mutex wrapper it gives you goes out of scope.
1
Mutex<T> is ever so slightly more painful to use than RefCell<T> for the single-threaded case, because if a thread panics it poisons the mutexes as it unwinds (to avoid inconsistent state). So instead of borrow() you do get().unwrap() which propagates the failure across threads.
1
If you don't have unwinding, which Rust doesn't require, you don't need that, so in a kernel Mutex wouldn't actually be any more verbose than the single-threaded version of shared ownership mutability (RefCell). There's also Cell, if you only need mutation and not mutable refs.
1
So what returning Option<T> there does propagate the failure if a thread actually panics while holding the mutex lock, since it might have left whatever it's protecting in an inconsistent state. If you just abort on panic, you don't need that part of the API design.
1
In general, you're encouraged to communicate across threads via channels, but there's still a lot of use for atomic reference counting / shared data (especially for performance), and it's an interesting example of how safety is provided via these types and the Send/Sync traits.
1
If you're trying to check higher level correctness with further static analysis, it's much more feasible for code verified as being free of memory safety, type confusion, data races, etc. It has to be much more structured to pass compiler type checks and is a foundation for more.
1
You can also use the type system to provide safety beyond memory safety, the language just doesn't mandate / enforce opting into it. For example, an approach like github.com/iliekturtles/u for verifying units in physics code if it's a useful way of catching bugs in your domain.
Show replies