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

时间:2017-05-25 20:27:30

标签: 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....
    }
}

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

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

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

1 个答案:

答案 0 :(得分:6)

实现void f2(A* const a2)(此处附带Copy)只能解决问题,因为实现它会允许编译器隐式复制Clone结构,将复制的值传递给线程。

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

move

thread::spawn(move || { // ^^^^ HERE handle_client(stream, &conf); }); 关键字的完整目的是告诉编译器&#34;不,不要尝试推断变量在这个闭包中的使用方式,只需移动一切都在&#34;。

当你move move时,编译器说&#34;好的,我将&conf移动到闭包中然后引用它&#34;。< / p>

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

即可
move

如果您确实需要能够使用thread::spawn(|| { handle_client(stream, &conf); }); 关键字传递参考,则需要移动参考

move

这仍然不允许您的代码编译,因为不能保证引用超过线程。这在Passing a reference to a stack variable to a scoped thread中进行了详尽的讨论。