为什么将价值转移到这里而不是借来了? [英] Why is the value moved into the closure here rather than borrowed?

查看:78
本文介绍了为什么将价值转移到这里而不是借来了?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

错误处理一章 Rust Book的示例中包含有关如何使用 Option Result 的组合器的示例。读取文件,并通过应用一系列组合器,将内容解析为 i32 并返回到 Result< i32,String>
现在,我在看代码时感到困惑。在那里,在对and_的一个闭合中,创建了一个本地 String 值,随后将其作为返回值传递给另一个组合器。



这是代码示例:

 使用std :: fs :: File; 
使用std :: io :: Read;
使用std :: path :: Path;

fn file_double< P:AsRef< Path>>(file_path:P)->结果< i32,字符串> {
File :: open(file_path)
.map_err(| err | err.to_string())
.and_then(| mut file | {
let mut contents = String: :new(); //本地值
file.read_to_string(& mut contents)
.map_err(| err | err.to_string())
.map(| _ |内容) //移动时没有'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),
}
}

我指的是 contents 。它已创建,以后在映射组合器中引用,该组合器应用于 std :: io :: Result< usize> 返回 Read :: read_to_string 的值。
问题:我认为 move 标记关闭会默认借用任何参考值,从而导致借用检查人员抱怨说,内容的寿命不足。但是,此代码可以正常编译。这意味着 String contents 被移入并随后移出了闭包。为什么在没有显式移动的情况下完成此操作?

解决方案


我认为用move标记闭合将默认借用任何参考值,


不完全是。编译器会对闭包主体中的代码进行一些检查,并跟踪如何使用封闭变量。



当编译器看到方法时是在变量上调用的,然后它会查看接收者是什么类型( self & self & mut self )。当使用变量作为参数时,编译器还会通过值,引用或可变引用来跟踪它。

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



在这种情况下,变量从闭包中返回,这必须表示该变量已按值使用。因此,变量将自动移动到闭包中。






有时 move 关键字太大了,因为它会将所有引用的变量都移入其中。有时您可能只想强制移入一个变量而不移入其他变量。在那种情况下,我所知道的最好的解决方案是做一个明确的引用并将引用移入:

  fn main() {
让a = 1;
令b = 2;

{
令b =&b;
needs_to_own_a(移动|| a_function(a,b));
}
}


The Error Handling chapter of the Rust Book contains an example on how to use the combinators of Option and Result. A file is read and through application of a series of combinators the contents are parsed as an i32 and returned in a Result<i32, String>. Now, I got confused when I looked at the code. There, in one closure to an and_then a local String value is created an subsequently passed as a return value to another combinator.

Here is the code example:

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),
    }
}

The value I am referring to is contents. It is created and later referenced in the map combinator applied to the std::io::Result<usize> return value of Read::read_to_string. The question: I thought that not marking the closure with move would borrow any referenced value by default, which would result in the borrow checker complaining, that contents does not live long enough. However, this code compiles just fine. That means, the String contents is moved into, and subequently out of, the closure. Why is this done without the explicit move?

解决方案

I thought that not marking the closure with move would borrow any referenced value by default,

Not quite. The compiler does a bit of inspection on the code within the closure body and tracks how the closed-over variables are used.

When the compiler sees that a method is called on a variable, then it looks to see what type the receiver is (self, &self, &mut self). When a variable is used as a parameter, the compiler also tracks if it is by value, reference, or mutable reference. Whatever the most restrictive requirement is will be what is used by default.

Occasionally, this analysis is not complete enough — even though the variable is only used as a reference, we intend for the closure to own the variable. This usually occurs when returning a closure or handing it off to another thread.

In this case, the variable is returned from the closure, which must mean that it is used by value. Thus the variable will be moved into the closure automatically.


Occasionally the move keyword is too big of a hammer as it moves all of the referenced variables in. Sometimes you may want to just force one variable to be moved in but not others. In that case, the best solution I know of is to make an explicit reference and move the reference in:

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

    {
        let b = &b;
        needs_to_own_a(move || a_function(a, b));
    }
}

这篇关于为什么将价值转移到这里而不是借来了?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆