正如 Headers 所述,我希望从一个具有初始可变状态的函数返回一个闭包 . 在以下示例中, 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; })
这对我来说完全是个谜 .
2 回答
你需要在这里做两件事:make
res
mutable,并返回FnMut
闭包,而不是Fn
:Fn
trait禁止实现者在调用时自行更改 . 由于这个闭包正在修改它自己的状态,这意味着它不能是Fn
[1] . 相反,您需要使用FnMut
,它允许突变闭包捕获的环境 .[1]:当然,除非你涉及内部可变性 .
DK.已经说过如何修复
agg1
,但我想解释agg0
的问题,为了完整性 .我们可以从
agg0
's return type that the type of the closure'推断出第二个参数是&mut i32
.&mut acc
是一种解构可变引用的模式,将acc
定义为i32
,初始化为引用值的副本 . 您可以't mutate it, because you didn' t将acc
定义为可变(您需要编写&mut mut acc
而不是&mut acc
),但's not what you want anyway, because then you' d正在改变副本 . 你想要的是改变指向的整数,所以你需要像这样定义你的闭包:这里,
acc
的类型是&mut i32
,所以为了改变i32
,我们需要首先取消引用指针(这会产生一个引用指针后面的i32
的左值;它不是副本!) .