使用带有 Rc 的闭包时,无法借用 Fn 闭包中捕获的外部变量

use*_*932 6 rust

use std::rc::Rc;

fn f1(cb: Box<Fn(i32) -> i32>) {
    let res = cb(15);
    println!("res {}", res);
}

fn main() {
    let mut v2 = Rc::new(5_i32);

    // 1
    // f1(Box::new(move |x: i32| *v2 + x));

    // 2
    f1(Box::new(move |x: i32| {
        let tmp = *v2;
        *Rc::get_mut(&mut v2).unwrap() = tmp + 1;
        x + *v2
    }));
}
Run Code Online (Sandbox Code Playgroud)

如果未注释,引用为“1”的代码可以正常编译并运行,但引用为“2”的代码无法编译,失败并显示消息:

use std::rc::Rc;

fn f1(cb: Box<Fn(i32) -> i32>) {
    let res = cb(15);
    println!("res {}", res);
}

fn main() {
    let mut v2 = Rc::new(5_i32);

    // 1
    // f1(Box::new(move |x: i32| *v2 + x));

    // 2
    f1(Box::new(move |x: i32| {
        let tmp = *v2;
        *Rc::get_mut(&mut v2).unwrap() = tmp + 1;
        x + *v2
    }));
}
Run Code Online (Sandbox Code Playgroud)

如果我想保持代码结构不变,我该如何解决这个问题?

在我的真实代码中,我想连接两个特征。其中一个将调用事件回调,另一个有一个函数来处理回调:

trait Foo {
    fn add_callback(&mut self, cb: Box<Fn(i32)>);
}

trait Boo {
    fn on_new_data(&mut self, data: i32);
}
Run Code Online (Sandbox Code Playgroud)

我想用 创建一个特征对象Boo,用 包装它Rc,然后Foo::add_callback|x:i32| Rc::get_mut(&mut boo).unwrap().on_new_data(x)

She*_*ter 4

整个错误消息大多有帮助:

error[E0596]: cannot borrow `v2` as mutable, as it is a captured variable in a `Fn` closure
  --> src/main.rs:17:19
   |
17 |      *Rc::get_mut(&mut v2).unwrap() = tmp + 1;
   |                   ^^^^^^^ cannot borrow as mutable
   |
help: consider changing this to accept closures that implement `FnMut`
  --> src/main.rs:15:17
   |
15 |       f1(Box::new(move |x: i32| {
   |  _________________^
16 | |      let tmp = *v2;
17 | |      *Rc::get_mut(&mut v2).unwrap() = tmp + 1;
18 | |      x + *v2
19 | |     }));
   | |_____^
Run Code Online (Sandbox Code Playgroud)

更改f1为接受 aFnMut并使变量可变允许代码编译:

fn f1(mut cb: Box<FnMut(i32) -> i32>) {
Run Code Online (Sandbox Code Playgroud)

这是为了改变捕获的变量所必需的,这是参数 tov2所需要的。&mut v2Rc::get_mut