为什么 Rust 会推断 FnMut 而不是 FnOnce 来表示此闭包,即使推断 FnMut 会导致错误?

Why does Rust infer FnMut instead of FnOnce for this closure, even though inferring FnMut causes an error?

提问人:pycache 提问时间:12/16/2022 更新时间:11/13/2023 访问量:112

问:

尝试编译此代码:

fn main() {
    fn id(x: &mut u8) -> &mut u8 { x }
    let x = &mut 0_u8;
    let f = move || id(x);
}

导致错误:

error: captured variable cannot escape `FnMut` closure body
 --> main.rs:4:21
  |
4 |     let f = move || id(x);
  |                   - ^^^^^ returns a reference to a captured variable which escapes the closure body
  |                   |
  |                   inferred to be a `FnMut` closure
  |
  = note: `FnMut` closures only have access to their captured variables while they are executing...
  = note: ...therefore, they cannot allow references to captured variables to escape

但是,如果我强制将闭包推断为 only (not ),如下所示:fFnOnceFnMut

fn main() {
    fn id(x: &mut u8) -> &mut u8 { x }
    let x = &mut 0_u8;
    fn force_fn_once<F: FnOnce() -> R, R>(f: F) -> F { f }
    let f = force_fn_once(move || id(x));
}

然后编译示例。我已经在 Rust 1.65.0 以及当时最新的每晚版本 1.68.0-nightly (2022-12-14) 上对此进行了测试,并且有和没有声明关闭为 .move

为什么 Rust 编译器会推断何时导致错误(以及何时可以简单地推断以避免该错误)?而且,作为后续问题,有没有更好的方法来强制编译器进行推断?FnMutfFnOnceFnOnce

Rust 闭合类型 推断

评论

0赞 Chayim Friedman 12/16/2022
@kmdreko是的,我认为你是对的。

答:

2赞 kmdreko 12/16/2022 #1

我相信,在没有推理的情况下,编译器试图将闭包的功能从限制性最小到限制性最强( -> -> )。它会看到它无法工作,因为它需要移动或可变地重新借用,但它可以成功进行类型检查,因为它可以可变地重新借用以传递给 。FnFnMutFnOnceFnxFnMutxid()

但是,借用检查器仅在类型推导之后运行,并且只有这样,“转义捕获”才会发出错误。编译器不会回溯以使用不同的函数特征再次尝试。

有没有更好的方法来强制编译器进行推断?FnOnce

类似的东西最终可能会可用,但在此之前,在这种情况下,您可以引入一个语句来强制移动捕获的变量:let f: impl FnOnce() -> _ = ...

let f = move || { let x = x; id(x) };
               // ^^^^^^^^^^

编辑:https://github.com/rust-lang/rust-clippy/issues/11562#issuecomment-1750237884 建议一个更简洁的形式:

let f = move || id({ x });

评论

0赞 kmdreko 12/16/2022
我将尝试查看是否存在已经报告此缺陷的 Rust 问题。虽然这听起来确实有点棘手。