My understanding is that it's not possible to share mutable state between tasks in Rust, so why does Rust has things like mutexes in the language? What's their purpose?
-
I think you might be laboring under some invalid assumptions. It says right in the page you linked that a Mutex is "A mutual exclusion primitive useful for protecting *shared data*" – Robert Harvey Apr 28 '14 at 20:56
-
Everything I've seen though indicates that there is no shared mutable state between tasks though, it's all done via message passing. So either there _is_ shared state capability and I can't find any info on it anywhere, or these primitives aren't useful for anything. – gct Apr 28 '14 at 21:01
-
Is it possible that the message passing requires synchronization? – Robert Harvey Apr 28 '14 at 21:02
-
In fact the official [FAQ](https://github.com/graydon/rust/wiki/Doc-language-FAQ) says explicitly: "Mutability control. Immutable by default. No shared mutable state across tasks." – gct Apr 28 '14 at 21:02
-
1*by default* being the operative words, there. It is an intriguing question, though. – Robert Harvey Apr 28 '14 at 21:02
-
That doesn't pertain to the third sentence though. That just means that variable declarations are by default the equivalent of const in C. – gct Apr 28 '14 at 21:04
-
1@gct: `Mutex` and other such things are safe abstractions over the unsafe behaviour of sharing mutable state across tasks. The language itself does not support mutable state. – Chris Morgan Apr 28 '14 at 22:32
-
@ChrisMorgan: Why does it have the `mut` keyword then? – Robert Harvey Apr 29 '14 at 17:27
-
1@RobertHarvey: sorry, I meant *shared* mutable state. – Chris Morgan Apr 30 '14 at 00:07
2 Answers
"Sharing mutable data between tasks is not allowed" is an oversimplification. No offense meant, it's also used in much introductory material on Rust, and for good reasons. But the truth is, Rust just wants to get rid of data races; not sharing anything is the preferred approach but not the only. Rust also wants to be a system programming language in the same sense as C and C++ are, so it won't nilly-willy completely remove some capability or performance optimization. However, in general shared mutable memory is not safe (data races etc.) so if you want it, you will have to acknowledge the responsibility by wrapping it in unsafe
blocks.
Luckily, some patterns of using shared mutable memory are safe (e.g. using proper locking discipline). When these patterns are recognized and considered important enough, someone writes some unsafe
code that they convince themselves (or perhaps even "prove") exposes a safe interface. In other words: Code using the interface can never violate the various safety requirements of Rust. For example, while Mutex
allows you to access mutable memory from different tasks at different times, it never permits aliasing among tasks (i.e. access at the same time), so data races are prevented.
Rust defines a Mutex
as
A mutual exclusion primitive useful for protecting shared data
A clear example of Mutex
use can be found in the Mutex
documentation. Note the use of the mut
keyword to designate mutable variables:
use std::sync::{Arc, Mutex};
use std::thread;
use std::sync::mpsc::channel;
const N: usize = 10;
// Spawn a few threads to increment a shared variable (non-atomically), and
// let the main thread know once all increments are done.
//
// Here we're using an Arc to share memory among threads, and the data inside
// the Arc is protected with a mutex.
let data = Arc::new(Mutex::new(0));
let (tx, rx) = channel();
for _ in 0..10 {
let (data, tx) = (data.clone(), tx.clone());
thread::spawn(move || {
// The shared state can only be accessed once the lock is held.
// Our non-atomic increment is safe because we're the only thread
// which can access the shared state when the lock is held.
//
// We unwrap() the return value to assert that we are not expecting
// threads to ever fail while holding the lock.
let mut data = data.lock().unwrap();
*data += 1;
if *data == N {
tx.send(()).unwrap();
}
// the lock is unlocked here when `data` goes out of scope.
});
}
rx.recv().unwrap();
Rust also provides an unsafe
keyword. Unsafe operations are those that potentially violate the memory-safety guarantees of Rust's static semantics. So the guarantee of immutable safety is by no means assured.

- 388,571
- 95
- 1,107
- 1,366

- 178,213
- 47
- 333
- 501