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
source to share
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 .
source to share