如何从Rust的Fn闭包内部更改变量? [英] How to change the variable from inside Fn closure in Rust?
问题描述
我有以下代码(游乐场):
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
函数以使用 Fn
:
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屋!