Why do I need to implement `Copy` and` Clone` if I am using a multi-stream immutable link?

Still struggling with Rust's mind shift, I now have this use case - one configuration for multithreading 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....
    }
}

      

I'm glad it is TcpStream

consumed handle_client

that it is intended, but why would Configuration

one need to copy for each thread? I would like to have only one copy and share the immutable link with all streams. Is it possible? Or maybe I'm missing the point.

Why do I need traits Copy

and Clone

if I pass Configuration

by reference? This confused me a lot:

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

      

+3


source to share


1 answer


The implementation Copy

( Clone

accidental here) fixes the problem only because its implementation allows the compiler to implicitly duplicate the structure Configuration

by passing the copied value to the stream.

He needs to pass the variable by value because you told the compiler move

all the values ​​used in the closure:

thread::spawn(move || {
//            ^^^^ HERE
    handle_client(stream, &conf);
});

      

The whole purpose of the keyword move

is to tell the compiler "no, don't try to infer how the variables are being used in this closure, just move everything into".

When you do move

&conf

, the compiler says "OK, I'll move conf

to the closure and then link to it."



In your case, you can simply remove the keyword move

:

thread::spawn(|| {
    handle_client(stream, &conf);
});

      

If you really need to use the link move

keyword and in the link, you need to move a link:

let conf = &conf;
thread::spawn(move || {
    handle_client(stream, conf);
});

      

This still prevents your code from compiling, because there is no guarantee that the link survives the stream. This is discussed in detail in Passing a Stack Variable Reference to a Scoped Scope .

+6


source







All Articles