为什么我可以在Rust中将static&str“移动”到多个线程中?

the*_*ian 3 concurrency multithreading rust

我想更好地理解以下Rust代码背后的语义:

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();
}
Run Code Online (Sandbox Code Playgroud)

我不明白为什么要编译此代码。我mutable在多个线程之间共享了该变量,甚至对其进行了突变。内存在幕后到底发生了什么?我们是否在静态内存中使多个指针指向同一字符串?我们是否在内存中放置了两个不同的静态字符串?我可以让两个线程从同一个不可变项中读取数据,这一事实并不令我感到惊讶,但是让两个线程从一个可变变量中读取数据确实令我惊讶。

请注意,即使线程3在4之前运行,线程4也不反映线程3在其println!语句中设置的更新字符串。最后,由于我没有通过using &immutable,这是否意味着该正在“移动”到每个线程中,而不是实际的内存地址中?

Sve*_*ach 7

我在多个线程之间共享了相同的可变变量,甚至对其进行了突变。

不,您已将对同一静态字符串的引用复制到多个线程中。该引用指向一个不变的静态字符串,该字符串不能被突变。只读引用是Copy,因此您可以将它们移动到多个闭包中。

内存在幕后到底发生了什么?

字符串切片实际上是指向内存中字符串开头以及长度的指针。您的变量mutableimmutable仅包含这两部分信息,并且只有这两部分是可变的mutable。变量指向的实际字符串是不可变的。当将变量“移动”到闭包中时,实际上是将它们复制,因为&strCopy。复制的唯一信息是指针和长度,而不是实际的字符串数据。您最终会获得指向同一只读内存的多个指针,这不允许任何数据争用并且符合Rust的内存安全规则。

另外,请注意,即使线程3在4、4之前运行,也不会反映线程3在其println中设置的更新字符串!声明。

您仅修改指针的副本和长度。在线程3中,它mutable成为闭包局部的一个单独变量,您只需对其进行修改。

最后,由于我没有通过using &immutable,这是否意味着该值正在“移动”到每个线程中,而不是实际的内存地址中?

变量immutable的类型为&'static str,因此它已经是引用;&immutable将是对参考的参考。