以下示例代码无法编译:

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

推荐答案

我认为这个问题是由于f参数中隐含的生命周期造成的.就好像你写了这样的话:

fn invoke<'a>(i: i32, mut f: impl FnMut(i32) + 'a) {
    f(i)
}

将函数存储在循环外部时,编译器必须 Select 适用于all invocations in the whole function.single生存期

(看待它的另一种方法是,该参数的具体类型将是一个匿名 struct ,如struct AnonymousType<'a>,实现FnMut(i32).重要的是,无论您如何看待它,推导出的满足impl FnMut(i32)的具体类型将包含生存期,因为sum是通过闭包中的引用捕获的.)

生存期不能仅限于循环的单个迭代,因为该生存期不会应用于所有其他迭代.因此,编译器必须 Select 更长的生存期--但这会导致独占borrow 重叠的问题,这就是您所观察到的.

let f行移动到循环中允许编译器为每次迭代 Select 不同的生存期,因为每次迭代也会产生不同的f.

特别要注意的是,Rust中的函数指针和闭包当前不允许是泛型的,因此f不能包含在隐藏生存期内是泛型的函数指针.该功能可以在以后添加,如果是这样的话,将允许编译此代码.

Rust相关问答推荐

如何在原始字符串中转义";#和#";

抽象RUST中的可变/不可变引用

如何仅使用http机箱发送http请求?

新创建的变量的绑定生存期

在Rust中有没有办法在没有UB的情况下在指针和U64之间进行转换?

在铁 rust 中传递所有权

S在Cargo.toml中添加工作空间开发依赖关系的正确方法是什么?

为什么基于高山Linux的Docker镜像不能在绝对路径下找到要执行的命令?

如何将映射反序列化为具有与键匹配的字段的定制 struct 的向量?

在文件链实施中绕过borrow 判断器

对reqwest提供的这种嵌套JSON struct 进行反序列化

期望一个具有固定大小 x 元素的数组,找到一个具有 y 元素的数组

从Rust 的临时文件中创建引用是什么意思?

不安全块不返回预期值

如何在 Rust 中将 Vec> 转换为 Vec>?

判断对象是 PyDatetime 还是 Pydate 的实例?

仅当满足外部条件时如何添加到 actix web 的路由

使用部分键从 Hashmap 中检索值

火箭整流罩、tokio-scheduler 和 cron 的生命周期问题

如何在 Rust 的内置函数上实现特征?