Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can I "move" a static &str into multiple threads in Rust?

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?

like image 934
the_endian Avatar asked Oct 12 '18 02:10

the_endian


1 Answers

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.

like image 178
Sven Marnach Avatar answered Sep 28 '22 07:09

Sven Marnach