如何改变回调中的值?

Dav*_*lsh 2 rust

对于这个基本问题真的很抱歉。

我正在尝试模拟 Web 服务器调用之类的东西并从外部范围捕获变量。

fn http_get(path: &str, f: &dyn Fn()) {
  f();
}


fn main() {
  let mut counter = 0;

  http_get("/foo", &|| {
    counter = counter + 1;
    println!("/foo: {}", counter);
  });
  
  http_get("/bar", &|| {
    counter = counter + 1;
    println!("/bar: {}", counter);
  });
}
Run Code Online (Sandbox Code Playgroud)

这会导致编译器错误cannot assign to 'counter', as it is a captured variable in a 'Fn' closure

我知道我在一些基本的地方失败了,我有机会朝正确的方向推动吗?

Max*_*nko 5

首先,解释一下为什么这不起作用。

当您将您的内容转移counter到闭包中时,您基本上允许其他代码随时使用它。如果它只是一个闭包,您可以将move其放入闭包中,将值的所有权赋予内部函数,并且它将正常工作,包括突变,因为没有办法违反借用检查器规则。

现在,当您有两个必须使用相同值的闭包,并且您希望在counter两个闭包中都具有对 的可变引用时,借用检查器无法知道何时删除此可变引用。使用闭包的函数可以保存闭包供以后使用。然后,您将同时有两个对同一值的可变引用,这是借用检查器不允许的。

对于这种情况有几种解决方案。

  1. 如果您不跨线程发送闭包,则可以使用RefCell. 它提供了内部可变性,因此即使您不传递对闭包的可变引用,您仍然可以改变 的值counter。在这种情况下,借用检查器规则在运行时进行检查。如果您的代码尝试同时获取两个可变引用,而不删除前一个引用,那么您的代码将会panic
fn http_get(path: &str, f: &dyn Fn()) {
    f();
}

fn main() {
    let counter = 0;
    let r = std::cell::RefCell::new(counter);

    http_get("/foo", &|| {
        let mut counter = r.borrow_mut();
        *counter = *counter + 1;
        println!("/foo: {}", counter);
    });

    http_get("/bar", &move || {
        let mut counter = r.borrow_mut();
        *counter = *counter + 1;
        println!("/bar: {}", counter);
    });
}
Run Code Online (Sandbox Code Playgroud)
  1. 如果您需要跨越线程边界,您可以使用ArcandMutex正如@Zeppi 答案所建议的那样。

  2. 如果您使用多个线程并且需要改变的值就像 一样简单counter,那么您可能应该使用Arc原子。它们允许从多个线程进行可变访问而无需锁定。