如何将捕获的可变变量的回调视为普通的可变借用?

时间:2016-06-25 10:30:50

标签: callback rust

可以使用Foo方法修改

.modify()

struct Foo;
impl Foo {
    fn modify(&mut self) {}
}

Bar存储回调:

struct Bar<'a> {
    callback: Box<FnMut() + 'a>,
}
impl<'a> Bar<'a> {
    fn new<F: FnMut() + 'a>(f: F) -> Bar<'a> {
        Bar {
            callback: Box::new(f),
        }
    }
}

init()需要Bar切片并执行回调:

fn init(bars: &mut [Bar]) {
    for b in bars {
        (*b.callback)();
    }
}

现在最有趣的是:

在循环中更改Foo可以正常工作;在循环foo的每次迭代中,可以相互借用并调用.modify()

fn main() {
    let mut foo = Foo;

    for _ in 0..10 {
        foo.modify();
    }
}

更改回调中的Foo不起作用:

fn main() {
    let mut foo = Foo;

    let mut bar1 = Bar::new(|| foo.modify());
    let mut bar2 = Bar::new(|| foo.modify());

    init(&mut [bar1, bar2]);
}

Try it on the playground, 它有一个错误:

error[E0499]: cannot borrow `foo` as mutable more than once at a time
  --> src/main.rs:27:29
   |
26 |     let mut bar1 = Bar::new(|| foo.modify());
   |                             -- --- previous borrow occurs due to use of `foo` in closure
   |                             |
   |                             first mutable borrow occurs here
27 |     let mut bar2 = Bar::new(|| foo.modify());
   |                             ^^ --- borrow occurs due to use of `foo` in closure
   |                             |
   |                             second mutable borrow occurs here
...
30 | }
   | - first borrow ends here

如何为第2项实施类似的保证?

1 个答案:

答案 0 :(得分:4)

您可以使用RefCell

let foo = RefCell::new(Foo);

{
    let bar1 = Bar::new(|| foo.borrow_mut().modify());
    let bar2 = Bar::new(|| foo.borrow_mut().modify());
    init(&mut [bar1, bar2]);
}

let mut foo = foo.into_inner(); // extract foo to use in external API

小心使用borrow_mut(),如果当前借用了值,就会感到恐慌。

如果您可以更改Barinit(),则可以将值foo传递给与init()方法分开的modify()

struct Bar<'a> {
    callback: Box<FnMut(&mut Foo) + 'a>,
}
impl<'a> Bar<'a> {
    fn new<F: FnMut(&mut Foo) + 'a>(f: F) -> Bar<'a> {
        Bar {
            callback: Box::new(f),
        }
    }
}

fn init(bars: &mut [Bar], arg: &mut Foo) {
    for b in bars {
        (*b.callback)(arg);
    }
}
let mut bar1 = Bar::new(|x| x.modify());
let mut bar2 = Bar::new(Foo::modify); // you can pass it without closure
init(&mut [bar1, bar2], &mut foo);