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

时间:2018-08-05 22:16:19

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

这是handle_connection的功能签名:

fn handle_connection(mut stream: TcpStream) {}

为什么这里不使用move?是什么导致封闭中需要move

1 个答案:

答案 0 :(得分: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

或者如果闭包通过值返回对象:

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

因此,通常不必注释move关键字即可明确告知编译器。

但是如果闭包仅通过引用使用环境中的值,则编译器会认为不需要将该变量移至闭包中。但是出于另一个原因,可能仍然有必要:终身。示例:

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

在这种情况下,编译器只会看到s通过引用以只读方式使用(println不使用其参数)。因此,编译器不会使闭包成为移动闭包。但这会导致生命周期错误,因为s现在位于get_printer的堆栈框架中,而闭包的寿命超过了该堆栈框架。因此,在这种情况下,您必须通过添加move来强制编译器将环境移至闭包中:

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