为什么即使关闭取得值的所有权,也不总是需要move关键字?

g.d*_*ado 7 rust

在阅读Rust书的最后一章时,我不禁注意到move在封闭中没有使用它:

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);
        });
    }
}
Run Code Online (Sandbox Code Playgroud)

这是函数签名handle_connection:

fn handle_connection(mut stream: TcpStream) {}
Run Code Online (Sandbox Code Playgroud)

为什么不在move这里使用?什么会导致move从接线盒中必需的?

Luk*_*odt 8

Rust可以判断闭包何时以需要移动的方式使用环境中的值.就像调用一个按值获取参数的函数(你的handle_connection情况):

let s = String::from("hi");
let c = || drop(s);  // <-- `drop()` takes its argument by value
                     //      Thus, the compiler knows `c` is a move closure
Run Code Online (Sandbox Code Playgroud)

或者,如果闭包按值返回对象:

let s = String::from("hi");
let c = || s;  // <-- `s` is returned (FnOnce() -> String)
               //      Thus, the compiler knows `c` is a move closure
Run Code Online (Sandbox Code Playgroud)

通常,您不必注释move关键字以明确告诉编译器.

但是,如果闭包仅通过引用使用环境中的值,则编译器假定不必将该变量移动到闭包中.但它可能仍然需要另一个原因:生命.例:

fn get_printer(s: String) -> Box<Fn()> {
    Box::new(|| println!("{}", s))
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,编译器只看到s通过引用以只读方式使用(println不使用其参数).因此编译器不会使闭包成为闭包.但是这会导致生命周期错误,因为s现在生活在堆栈框架中,get_printer并且封闭框架的封闭寿命更长.因此,在这种情况下,您必须强制编译器通过添加move以下内容将环境移动到闭包中:

fn get_printer(s: String) -> Box<Fn()> {
    Box::new(move || println!("{}", s))
}
Run Code Online (Sandbox Code Playgroud)