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 module, 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 Readers 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.