如果我与多个线程共享一个不可变引用,为什么我需要实现`Copy`和`Clone`?

Rbj*_*bjz 3 multithreading reference rust

仍在努力应对Rust的思维转变,现在我有了这个用例 - 一个多线程的配置TcpListener:

use std::net::{TcpListener, TcpStream, ToSocketAddrs};
use std::thread;

fn main() {
    serve("127.0.0.1:3333", Configuration { do_something: true });
}

//#[derive(Copy, Clone)]
pub struct Configuration {
    pub do_something: bool,
}

pub fn serve<A: ToSocketAddrs>(addr: A, conf: Configuration) {
    let listener = TcpListener::bind(addr).expect("bind failed");

    for stream in listener.incoming() {
        match stream {
            Ok(stream) => {
                thread::spawn(move || {
                    handle_client(stream, &conf);
                });
            }
            Err(e) => {
                println!("Connection failed: {}", e);
            }
        }
    }
}

fn handle_client(stream: TcpStream, conf: &Configuration) {
    if conf.do_something {
        //stream....
    }
}
Run Code Online (Sandbox Code Playgroud)

我很高兴TcpStream消耗handle_client它,这是它的目的,但为什么Configuration必须为每个线程复制?我想只有一个副本,并与所有线程共享一个不可变的引用.那可能吗?或者也许我错过了这一点.

如果我通过引用传递,为什么我需要CopyClone特征Configuration?这让我很困惑:

error[E0382]: capture of moved value: `conf`
  --> src/main.rs:19:64
   |
19 |                 thread::spawn(move || { handle_client(stream, &conf); });
   |                               -------                          ^^^^ value captured here after move
   |                               |
   |                               value moved (into closure) here
   |
   = note: move occurs because `conf` has type `Configuration`, which does not implement the `Copy` trait
Run Code Online (Sandbox Code Playgroud)

She*_*ter 6

实现Copy(Clone这里是偶然的)只能修复问题,因为实现它允许编译器隐式复制Configuration结构,将复制的值传递给线程.

它需要按值传递变量,因为您告诉编译器将move所有使用的值放入闭包中:

thread::spawn(move || {
//            ^^^^ HERE
    handle_client(stream, &conf);
});
Run Code Online (Sandbox Code Playgroud)

整个宗旨的的move关键字告诉编译器"不,不要试图推断变量是如何关闭内部使用,只需将一切".

当你move &conf,编译器说"好的,我将conf进入闭包,然后参考它".

在您的情况下,您只需删除move关键字:

thread::spawn(|| {
    handle_client(stream, &conf);
});
Run Code Online (Sandbox Code Playgroud)

如果您确实需要能够使用move关键字传入引用,则需要移入引用:

let conf = &conf;
thread::spawn(move || {
    handle_client(stream, conf);
});
Run Code Online (Sandbox Code Playgroud)

这仍然不允许您的代码编译,因为不能保证引用超过线程.在将对堆栈变量的引用传递给作用域线程时,已经详细讨论了这一点.