如何从Rust的Fn闭包内部更改变量? [英] How to change the variable from inside Fn closure in Rust?

查看:66
本文介绍了如何从Rust的Fn闭包内部更改变量?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下代码(游乐场):

struct A {
    pub vec: Vec<u64>,
}

impl A {
    fn perform_for_all<F: Fn(&mut u64)>(&mut self, f: F) {
        for mut i in &mut self.vec {
            f(i);
        }
    }
}
fn main() {
    let mut a = A {
        vec: vec![1, 3, 44, 2, 4, 5, 6],
    };

    let mut done = false;

    a.perform_for_all(|v| {
        println!("value: {:?}", v);
        done = true;
    });

    if !done {
        a.perform_for_all(|v| {
            println!("value {:?}", v);
        });
    }
}

发生以下错误:

error[E0594]: cannot assign to `done`, as it is a captured variable in a `Fn` closure
  --> src/main.rs:21:9
   |
21 |         done = true;
   |         ^^^^^^^^^^^ cannot assign
   |
help: consider changing this to accept closures that implement `FnMut`
  --> src/main.rs:19:23
   |
19 |       a.perform_for_all(|v| {
   |  _______________________^
20 | |         println!("value: {:?}", v);
21 | |         done = true;
22 | |     });
   | |_____^

我有一个已加载对象的列表和一个数据库中的对象的列表.我需要一个函数,它需要一个闭包并在加载的对象上执行它,如果列表中没有对象,请在数据库中的对象列表上执行它.

I have a list of loaded objects and a list of objects in a database. I need a function that takes a closure and executes it on the loaded objects and if we don't have the objects in the list, execute it on a list of objects from the database.

该功能如下:

pub fn perform_for_match_with_mark<F>(&mut self, mark: MatchMark, f: F)
where
    F: Fn(&mut GameMatch),
{
    self.perform_for_all_matches(
        |m| {
            // runtime list
            if let Game::Match(ref mut gm) = *m {
                if gm.match_stamp().mark == mark {
                    f(gm);
                }
            }
        },
        None,
    );
    // if we have called `f` above - don't execute lines below.
    let tx = self.match_tx.clone();
    GamesDatabase::perform_for_match_with_mark(mark, |ms| {
        // database
        self.perform_for_all_matches(
            |m| {
                if let Game::Match(ref gm) = *m {
                    if gm.match_stamp().id == ms.id {
                        f(&mut GameMatch::new_with_match_stamp(
                            tx.clone(),
                            ms.clone(),
                            gm.needs_server_set,
                            gm.server_id,
                        ))
                    }
                }
            },
            None,
        );
    });
}

仅当我们无法在运行时列表中找到对象时,才必须对数据库中的对象进行操作.这就是为什么我决定创建一个变量,说我们已经在列表中找到了这些对象,不用管数据库了".

We have to operate on objects from the database only if we were unable to find them in runtime list. That is why I decided to make a variable which says "we already found these objects in the list, leave the database alone".

推荐答案

更改您的 perform_for_all 函数以使用

Change your perform_for_all function to use FnMut instead of Fn:

fn perform_for_all<F>(&mut self, mut f: F)
where
    F: FnMut(&mut u64),
{
    for mut i in &mut self.vec {
        f(&mut i);
    }
}

正如Peter所说的,正在发生一些编译器魔术.

As Peter said, there is some compiler magic going on.

Fn ::的签名:呼叫 是:

extern "rust-call" fn call(&self, args: Args) -> Self::Output

这采用了对 self 的不变引用,这就是为什么您不能修改任何捕获的变量的原因.

This takes an immutable reference to self, which is why you can't modify any of the captured variables.

FnMut ::的签名:call_mut 允许您对变量进行突变,因为它需要& mut self :

extern "rust-call" fn call_mut(&mut self, args: Args) -> Self::Output

通过将您的闭包从 Fn 更改为 FnMut ,您可以允许它修改其捕获的变量,因为传递给它的引用是可变的.

By changing your closure from Fn to FnMut, you allow it to modify its captured variables, given that the references you pass to it are mutable.

这篇关于如何从Rust的Fn闭包内部更改变量?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆