提问人:google2 提问时间:4/2/2023 最后编辑:google2 更新时间:4/2/2023 访问量:70
Rust 函数指针似乎被借用检查器视为有状态的
Rust function pointer seems to be treated as stateful by borrow checker
问:
以下示例代码不编译:
fn invoke(i: i32, mut f: impl FnMut(i32)) {
f(i)
}
fn main() {
let f: fn(i32, _) = invoke;
let mut sum: i32 = 0;
for i in 0..10 {
_ = f(i, |x| sum += x);
}
println!("{:?}", sum);
}
编译器返回以下错误:
Compiling playground v0.0.1 (/playground)
error[E0499]: cannot borrow `sum` as mutable more than once at a time
--> src/main.rs:10:18
|
10 | _ = f(i, |x| sum += x);
| - ^^^ --- borrows occur due to use of `sum` in closure
| | |
| | `sum` was mutably borrowed here in the previous iteration of the loop
| first borrow used here, in later iteration of loop
For more information about this error, try `rustc --explain E0499`.
error: could not compile `playground` due to previous error
如果我将赋值移动到循环中,代码将编译:f
for
fn invoke(i: i32, mut f: impl FnMut(i32)) {
f(i)
}
fn main() {
let mut sum: i32 = 0;
for i in 0..10 {
let f: fn(i32, _) = invoke;
_ = f(i, |x| sum += x);
}
println!("{:?}", sum);
}
我很困惑为什么第一个代码无法编译。变量的类型是 ,这意味着它是无状态的。变量也是不可变的,所以即使它的类型是有状态的,它也不能存储闭包。因此,编译器应该能够得出结论,在循环的下一次迭代之前,将删除闭包。然而,编译器的行为就像是可变的,它可以存储闭包。您能否解释一下为什么编译器会这样做。f
fn
f
for
f
rustc 版本:稳定版 v1.68.2
答:
5赞
cdhowie
4/2/2023
#1
我相信这个问题是由于参数中存在的隐含寿命造成的。就好像你写了这个:f
fn invoke<'a>(i: i32, mut f: impl FnMut(i32) + 'a) {
f(i)
}
将函数存储在循环外部时,编译器必须选择适用于整个函数中所有调用的单个生存期。
(另一种看待它的方式是,这个参数的具体类型将是一个匿名结构,如 实现 .重要的是,无论你如何看待它,推导出来满足的具体类型都将包含一个生命周期,因为在闭包中通过引用捕获。struct AnonymousType<'a>
FnMut(i32)
impl FnMut(i32)
sum
生存期不能限制为循环的单个迭代,因为该生存期不适用于所有其他迭代。因此,编译器必须选择更长的生存期,但这会导致独占借用重叠的问题,这就是您正在观察到的问题。
将行移动到循环中允许编译器为每次迭代选择不同的生存期,因为每次迭代也会存在不同的生存期。let f
f
特别要注意的是,Rust 中的函数指针和闭包目前不允许是泛型的,因此不能包含在隐藏生命周期内泛型的函数指针。该功能可以在以后添加,如果是这样,那么这将允许编译此代码。f
评论
f
f
fn
fn