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

Tes*_*est 4 rust

以下程序编译没有问题:

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

但是,如果行“return g(y).await;” 被删除后,它将失败并出现以下情况:

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 {
   |                      ++++
Run Code Online (Sandbox Code Playgroud)

为什么原来的代码中没有出现同样的错误呢?

Sil*_*olo 6

Rust 做了让你的闭包工作所需的最少量的工作。

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

这里,内部闭包需要y通过引用。所以 Rust 本质上将把它变成 astruct和 a &String。为了简单起见,删除异步的东西,它会变成这样

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

有效地进入这个

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

现在,在编译过程的后期'a,Rust 意识到for 的生命MyCustomClosure周期与封闭函数的生命周期不一致,并且它会抱怨。但到目前为止,它已经致力于在此处使用引用,并且还不够聪明,无法返回并尝试不同的闭包类型。这是两个不同的编译阶段,彼此不直接对话。

这另一方面

let f = || {
    let y: String = String::from("a").clone();
    || { y }
};
Run Code Online (Sandbox Code Playgroud)

另一方面,这显然需要采取行动。我们在闭包内传递所有权,因此我们得到一个仅实现FnOnce并按值获取的闭包y。本质上我们得到

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

现在,不再有终生争论'a会导致未来的冲突。只有一个简单的结构,一切顺利。

正如错误消息所示,如果您的意图是FnOnce通过移动来获取返回字符串,则可以在闭包前面加上move关键字。

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