为什么这里的值被移动到闭包中而不是借来的?

jte*_*epe 9 closures rust

错误处理章锈书包含有关如何使用的组合子的例子OptionResult。读取文件并通过应用一系列组合器将内容解析为i32并以Result<i32, String>. 现在,当我查看代码时,我感到困惑。在那里,在一个 and_then 的闭包中,String创建了一个局部值,随后作为返回值传递给另一个组合器。

下面是代码示例:

use std::fs::File;
use std::io::Read;
use std::path::Path;

fn file_double<P: AsRef<Path>>(file_path: P) -> Result<i32, String> {
    File::open(file_path)
         .map_err(|err| err.to_string())
         .and_then(|mut file| {
              let mut contents = String::new(); // local value
              file.read_to_string(&mut contents)
                  .map_err(|err| err.to_string())
                  .map(|_| contents) // moved without 'move'
         })
         .and_then(|contents| {
              contents.trim().parse::<i32>()
                      .map_err(|err| err.to_string())
         })
         .map(|n| 2 * n)
}

fn main() {
    match file_double("foobar") {
        Ok(n) => println!("{}", n),
        Err(err) => println!("Error: {}", err),
    }
}
Run Code Online (Sandbox Code Playgroud)

我所指的值是contents. 它被创建并随后在map应用于 的std::io::Result<usize>返回值的组合器中被引用Read::read_to_string。问题:我认为标记闭包move会默认借用任何引用的值,这会导致借用检查器抱怨,它的contents寿命不够长。但是,这段代码编译得很好。这意味着,将String contents移入并随后移出闭包。为什么在没有明确的情况下这样做move

She*_*ter 6

我认为使用 move 标记闭包会默认借用任何引用的值,

不完全的。编译器对闭包主体内的代码进行一些检查,并跟踪封闭变量的使用方式。

当编译器发现在变量上调用了一个方法时,它会查看接收者的类型(self, &self, &mut self)。当变量用作参数时,编译器还会跟踪它是按值、引用还是可变引用。无论最严格的要求是什么,都将是默认使用的要求。

有时,这种分析还不够完整——即使变量仅用作引用,我们也打算让闭包拥有该变量。这通常发生在返回闭包或将其交给另一个线程时。

在这种情况下,该变量是从闭包中返回的,这意味着它是按值使用的。因此变量将自动移动到闭包中。


偶尔move关键字是太大,因为它移动的锤子的所有被引用的变量。有时你可能只想强制一个变量中移动而不是其他。在这种情况下,我所知道的最佳解决方案是进行显式引用并将引用移入:

fn main() {
    let a = 1;
    let b = 2;

    {
        let b = &b;
        needs_to_own_a(move || a_function(a, b));
    }
}
Run Code Online (Sandbox Code Playgroud)