While reading the last chapter of the Rust book, I couldn't help but notice that move
was not used within a closure:
fn main() {
let listener = TcpListener::bind("127.0.0.1:7878").unwrap();
for stream in listener.incoming() {
let stream = stream.unwrap();
// move not used here
thread::spawn(|| {
handle_connection(stream);
});
}
}
Here is the function signature of handle_connection
:
fn handle_connection(mut stream: TcpStream) {}
Why isn't move
used here? What would cause move
to be required from within the closure?
Keyword move move converts any variables captured by reference or mutable reference to variables captured by value. move is often used when threads are involved. move is also valid before an async block.
Rust's closures are anonymous functions you can save in a variable or pass as arguments to other functions. You can create the closure in one place and then call the closure elsewhere to evaluate it in a different context. Unlike functions, closures can capture values from the scope in which they're defined.
Rust can tell when a closure uses a value from the environment in a way that requires a move. Like calling a function that takes the argument by value (your handle_connection
case):
let s = String::from("hi");
let c = || drop(s); // <-- `drop()` takes its argument by value
// Thus, the compiler knows `c` is a move closure
Or if the closure returns the object by value:
let s = String::from("hi");
let c = || s; // <-- `s` is returned (FnOnce() -> String)
// Thus, the compiler knows `c` is a move closure
So usually, you don't have to annotate the move
keyword to explicitly tell the compiler.
However, if the closure uses the value from the environment only via references, the compiler assumes that moving that variable into the closure is not necessary. But it might still be necessary for another reason: lifetimes. Example:
fn get_printer(s: String) -> Box<Fn()> {
Box::new(|| println!("{}", s))
}
In this case, the compiler only sees that s
is used in read only fashion via reference (println
doesn't consume its arguments). Thus the compiler doesn't make the closure a move closure. But this results in a lifetime error, because s
now lives in the stackframe of get_printer
and the closure outlives that stackframe. So in this case, you have to force the compiler to move the environment into the closure by adding move
:
fn get_printer(s: String) -> Box<Fn()> {
Box::new(move || println!("{}", s))
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With