简体   繁体   English

为什么 Rust 在必要时不会自动移动?

[英]Why does Rust not automatically move when necessary?

The following program compiles without issue:以下程序编译没有问题:

#[tokio::main]
async fn main() {
    async fn g(x: String) {}

    let f = || {
        let y: String = String::from("a").clone();

        return async {
        println!("{}", &y);
        return g(y).await;
    }};
}

However, if the line "return g(y).await;"但是,如果行“return g(y).await;” is removed, it will fail with the following:被删除,它将失败并显示以下内容:

error[E0373]: async block may outlive the current function, but it borrows `y`, which is owned by the current function
  --> src/main.rs:35:22
   |
35 |           return async {
   |  ______________________^
36 | |         println!("{}", &y);
   | |                         - `y` is borrowed here
37 | |         // return g(y).await;
38 | |     }};
   | |_____^ may outlive borrowed value `y`
   |
note: async block is returned here
  --> src/main.rs:35:16
   |
35 |           return async {
   |  ________________^
36 | |         println!("{}", &y);
37 | |         // return g(y).await;
38 | |     }};
   | |_____^
help: to force the async block to take ownership of `y` (and any other referenced variables), use the `move` keyword
   |
35 |         return async move {
   |                      ++++

Why does the same error not appear in the original code?为什么原代码中没有出现同样的错误?

Rust does the minimum amount of work necessary to get your closure to work. Rust 做了最少的工作来让你的闭包工作。

let f = || {
    let y: String = String::from("a").clone();
    return async {
        println!("{}", &y);
    }
};

Here, the inner closure requires y by reference.在这里,内部闭包需要y引用。 So Rust is going to turn it into, essentially, a struct with a &String .因此,Rust 本质上将把它变成一个带有&Stringstruct Removing the async stuff for simplicity, it would turn this为简单起见删除异步的东西,它会变成这个

let f = || {
    let y: String = String::from("a").clone();
    || {
        println!("{}", &y);
    }
};

into, effectively, this进入,有效地,这

struct MyCustomClosure<'a> { y: &'a String };

impl<'a> FnOnce for MyCustomClosure<'a> {
  fn call_once(self) {
    println!("{}", self.y)
  }
}

// (Same impl for FnMut and Fn ...)

let f = || {
    let y: String = String::from("a").clone();
    return MyCustomClosure { y: &y }
};

Now, sometime way later on in the compilation process, Rust realizes that the lifetime 'a for MyCustomClosure doesn't line up with the lifetime for the enclosing function, and it complains.现在,在编译过程稍后的某个时间,Rust 意识到MyCustomClosure的生命周期'a与封闭函数的生命周期不一致,它会抱怨。 But by this point it's already committed to using a reference here and it's not smart enough to go back and try a different closure type.但此时它已经承诺在此处使用引用,并且还不够聪明,无法返回并尝试不同的闭包类型。 It's two different stages of compilation that don't talk to each other directly.这是两个不同的编译阶段,彼此不直接对话。

This on the other hand另一方面,这

let f = || {
    let y: String = String::from("a").clone();
    || { y }
};

This, on the other hand, very clearly requires a move.另一方面,这显然需要采取行动。 We're passing ownership inside the closure, so we get a closure that only implements FnOnce and that takes the y by value.我们在闭包内部传递所有权,所以我们得到一个只实现FnOnce并且按值获取y的闭包。 Essentially we get基本上我们得到

struct MyCustomClosure2 { y: String };

impl FnOnce for MyCustomClosure2 {
  fn call_once(self) -> String {
    self.y
  }
}

// No FnMut or Fn this time, since we need to pass ownership of a value.

Now there's no lifetime argument 'a to cause conflicts down the road.现在没有终生争论'a导致未来的冲突。 There's just a simple struct and it all works out.只有一个简单的结构,一切都解决了。

As the error message indicates, if your intent is to get an FnOnce which returns the string by moving, you can prefix your closure with the move keyword.如错误消息所示,如果您的意图是获得一个通过移动返回字符串的FnOnce ,您可以在闭包前面加上move关键字。

let f = || {
    let y: String = String::from("a").clone();
    return async move {
        println!("{}", &y);
    }
};

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM