提问人:pycache 提问时间:12/16/2022 更新时间:11/13/2023 访问量:112
为什么 Rust 会推断 FnMut 而不是 FnOnce 来表示此闭包,即使推断 FnMut 会导致错误?
Why does Rust infer FnMut instead of FnOnce for this closure, even though inferring FnMut causes an error?
问:
尝试编译此代码:
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 ),如下所示:f
FnOnce
FnMut
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 编译器会推断何时导致错误(以及何时可以简单地推断以避免该错误)?而且,作为后续问题,有没有更好的方法来强制编译器进行推断?FnMut
f
FnOnce
FnOnce
答:
2赞
kmdreko
12/16/2022
#1
我相信,在没有推理的情况下,编译器试图将闭包的功能从限制性最小到限制性最强( -> -> )。它会看到它无法工作,因为它需要移动或可变地重新借用,但它可以成功进行类型检查,因为它可以可变地重新借用以传递给 。Fn
FnMut
FnOnce
Fn
x
FnMut
x
id()
但是,借用检查器仅在类型推导之后运行,并且只有这样,“转义捕获”才会发出错误。编译器不会回溯以使用不同的函数特征再次尝试。
有没有更好的方法来强制编译器进行推断?
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 问题。虽然这听起来确实有点棘手。
评论