In Rust, the language has a concept of types that are thread safe and can be shared, along with types that are safe to send between threads. For example, Rc<T> uses non-atomic reference counting and isn't Send. Arc<T> is a Send variant. Mutex<T> is a Sync variation of RefCell<T>.
Conversation
So you can use Arc<Mutex<T>> for thread-safe shared mutable data. It also supports sharing mutable data between threads via atomics or without any synchronization at all via the standard reference safety system which enforces that mutable references do not alias anything else.
2
1
So for example you can divide up an array into non-overlapping mutable slices (pointer + length views) sent to different threads with their lifetimes constrained by the compiler to not outlive the data. It prevents data races (not higher level race conditions) in the type system.
2
1
Things like that are decent initial step but as programmer with kernel background, not picking a fight here, type systems can be abused by inexperienced programmers. Sometimes type systems aren't the most direct or sustainable way of writing that type of code
2
Linux kernel driver layers can have rather complicated nested structures. Some are a hybrid of of two subsystems like USB and ALSA (audio). Both have smaller structures inside them per each subsystems. In the case of a class compliant USB audio driver, both subsystems 1/
2
This is just a bad driver model. There's no reason for a driver to have any complex data structures or structure lifetimes. Hardware is highly finite and static.
2
The OS infrastructure using/interfacing with the drivers should be the component responsible for awareness of connections between hardware/drivers, user or kernel space consumers using the hardware, etc. and driver should have no access to these structures.
1
Rust discourages having shared ownership (Rc, Arc), but there end up being use cases for it. It just isn't the normal way to design / implement things. It's painful to use Rust if you don't design things around simple ownership models because it forces it to be verified as safe.
1
1
The code is very clean if you have unique ownership and use lots of lightweight references. If you have complex data structures, like linked data structures with cycles and mutability, you are forced into using Rc / RefCell or a similar approach which really discourages doing it.
1
So for example, if you have a nice recursive tree data structure, you can represent that with the nodes as Box<T>, which is a uniquely owned dynamic allocation. If you want to have cycles back from the children to the parent nodes, you need Rc<T> with Weak<T> for parent pointers.
1
So when you are actually using the parent pointer, you call upgrade(), which gives you an Option<Rc<T>> (since it might have been deallocated). You can do it, but you're encouraged to do things in a more simple way by the requirement for safety, like avoiding reference cycles.
Honestly that's very complicated and would discourage me from using those conventions unless it's a project requirement or if the outcome was clearly positive.
I'll think about it more carefully. There's a lot I think I only partially understand here. 1/
2
And thank you for taking the time to write back and explain this to me 2/
If you didn't have generics, closures and sum types in addition to the ownership / lifetime system, you couldn't implement collections, iterators, reference counting, threads, synchronization, etc. in libraries. That's why I say doing it from scratch would reinvent most of it.


