正如标题所述,我希望从具有一些初始可变状态的函数中返回一个闭包。在以下示例中,CowRow
是一个带有 time
字段的 struct
。它还有一个 String
字段,因此不可复制。具体来说,我想要一个看起来像这样的函数:
pub fn agg1() -> Box<Fn(&CowRow)> {
let res = 0;
Box::new(move |r| { res += r.time; })
}
当然,这会产生错误:
src/queries.rs:9:25: 9:38 error: cannot assign to captured outer variable in an `Fn` closure
src/queries.rs:9 Box::new(move |r| { res += r.time; })
^~~~~~~~~~~~~
src/queries.rs:9:14: 9:41 help: consider changing this closure to take self by mutable reference
src/queries.rs:9 Box::new(move |r| { res += r.time; })
^~~~~~~~
据我了解,Rust 需要知道返回值的大小,并且因为闭包从它们的环境中借用了它们的栈帧,所以我们需要引入 Box
和 move
获取返回的大小并将闭包放在堆上。
在这个闭包环境中,有没有什么方法可以将 res
也放在堆上?或者以其他方式允许这种行为?我当然看过:Cannot borrow captured outer variable in an `Fn` closure as mutable但这似乎过于复杂,我不清楚在多个线程同时运行此功能的情况下这将如何执行。
我尝试的另一种技术是更改闭包以获取对 i32
的可变引用,我可以在 agg
函数之外对其进行初始化。示例:
pub fn agg0() -> Box<Fn(&CowRow, &mut i32)> {
Box::new(move |r, &mut acc| { acc += r.time; })
}
但是,这会产生错误:
src/queries.rs:4:35: 4:48 error: re-assignment of immutable variable `acc` [E0384]
src/queries.rs:4 Box::new(move |r, &mut acc| { acc += r.time; })
^~~~~~~~~~~~~
src/queries.rs:4:35: 4:48 help: run `rustc --explain E0384` to see a detailed explanation
src/queries.rs:4:28: 4:31 note: prior assignment occurs here
src/queries.rs:4 Box::new(move |r, &mut acc| { acc += r.time; })
这对我来说完全是个谜。
最佳答案
你需要在这里做两件事:使 res
可变,并返回一个 FnMut
闭包,而不是 Fn
闭包:
pub struct CowRow {
time: u64,
}
pub fn agg1() -> Box<FnMut(&CowRow) -> u64> {
let mut res = 0;
Box::new(move |r| { res += r.time; res })
}
fn main() {
let mut c = agg1();
let moo = CowRow { time: 2 };
println!("{:?}", c(&moo));
println!("{:?}", c(&moo));
println!("{:?}", c(&moo));
}
Fn
特性禁止实现者在被调用时改变自身。因为这个闭包正在修改它自己的状态,这意味着它不能是 Fn
[1]。相反,您需要使用 FnMut
,它确实允许改变闭包捕获的环境。
[1]:当然,除非你涉及内部可变性。
关于closures - 返回在 Rust 中捕获外部变量的闭包,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36511141/