You can use the type system to wrap an Arc<Mutex<T>>
in a way that disallows mutation except by one privileged owner. Here's an example:
use std::sync::Arc;
use std::sync::Mutex;
pub struct Writer<T>(Arc<Mutex<T>>);
impl<T> Writer<T> {
pub fn new(value: T) -> Self {
Writer(Arc::new(Mutex::new(value)))
}
pub fn reader(&self) -> Reader<T> {
Reader(Arc::clone(&self.0))
}
pub fn set(&self, value: T) {
*self.0.lock().unwrap() = value;
}
pub fn get(&self) -> T
where
T: Clone,
{
self.0.lock().unwrap().clone()
}
}
pub struct Reader<T>(Arc<Mutex<T>>);
// derive(Clone) uses incorrect bounds, so we must implement Clone manually
// (see https://stackoverflow.com/q/39415052/3650362)
impl<T> Clone for Reader<T> {
fn clone(&self) -> Self {
Reader(Arc::clone(&self.0))
}
}
impl<T> Reader<T> {
pub fn get(&self) -> T
where
T: Clone,
{
self.0.lock().unwrap().clone()
}
}
If you put this code in a mod
ule, Rust's privacy controls will prove that no user can duplicate a Writer
or turn a Reader
into a Writer
except through the use of unsafe
. Therefore you can clone and send Reader
s to as many threads as you like, but send the Writer
only to the particular thread that should have write access.
There are many possible variations on this design; for instance, you could use RwLock
instead of Mutex
to let multiple readers access the value simultaneously while it's not being written to.
Playground (based on Akiner Alkan's example)
Something like that would be possible in say C
Note that just as in Rust, if you want to do this safely in C, you need some kind of synchronization (a mutex or similar). Rust insists that you be explicit about how to avoid data races. C is different in that it will just assume you know what you're doing and then punish you savagely for writing races. In Rust the idiomatic approach is to use the safe abstractions provided by the standard library. However, if you have some other means of synchronization and can prove that Mutex
is unnecessary overhead, you can always just write things in the C way -- raw pointers are essentially the same in both Rust (within an unsafe
block) and C.