I'd like to better understand the semantics behind the following Rust code:
use std::thread;
fn main() {
let immutable = "I am not mutable";
let mut mutable = "I am mutable";
let handle1 = thread::spawn(move || {
println!("Thread 1 says: {}", immutable);
});
let handle2 = thread::spawn(move || {
println!("Thread 2 says: {}", immutable);
});
let handle3 = thread::spawn(move || {
println!("Thread 3 says: {}", mutable);
mutable = "go away";
});
let handle4 = thread::spawn(move || {
println!("Thread 4 says: {}", mutable);
});
handle1.join().unwrap();
handle2.join().unwrap();
}
I do not understand why this code compiles. I have shared the variable mutable
between multiple threads and even mutated it. What exactly is going on behind-the-scenes with the memory here? Are we making multiple pointers to the same string in static memory? Are we placing two different static strings in memory? The fact that I can have two threads reading from the same immutable item doesn't surprise me, but having two threads reading from a mutable variable does.
Note that even if thread 3 runs before 4, 4 does not reflect the updated string that thread 3 sets in its println!
statement. Finally, since I didn't pass using &immutable
, does this mean that the value is being "moved" into each thread rather than the actual memory address?
I have shared the same variable mutable between multiple threads and even mutated it.
No, you have copied a reference to the same static string into multiple threads. The reference points to a constant static string which can't be mutated. Read-only references are Copy
, so you can move them into multiple closures.
What exactly is going on behind-the-scenes with the memory here?
A string slice is essentially a pointer to the beginning of a string in memory together with a length. Your variables mutable
and immutable
only contain these two pieces of information, and only these pieces are mutable for mutable
. The actual strings the variables point to are immutable. When "moving" the variables into the closures, they are actually copied, since &str
is Copy
. The only information that is copied is the pointer and the length, not the actual string data. You end up with multiple pointers to the same read-only memory, which doesn't allow for any data races and conforms to Rust's memory safety rules.
Also, note that even if thread 3 runs before 4, 4 does not reflect the updated string that thread 3 sets in its println! statement.
You only modify the copy of the pointer and the length. In thread 3, mutable
becomes a separate variable local to the closure, and you only modify that.
Finally, since I didn't pass using
&immutable
, does this mean that the value is being "moved" into each thread rather than the actual memory address?
The variable immutable
has the type &'static str
, so it is already a reference; &immutable
would be a reference to a reference.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With