我有一个通过回调生成数据的结构。在回调内部,我需要修改结构、进程,然后取消修改。在回调结束时,该结构在语义上未修改,但代码具有 Rust 不喜欢的递归可变借用。
这是一个简化的示例:
struct A(u64);
impl A {
fn change(&mut self, x: u64) { self.0 ^= x; }
fn gen_data(&self, mut cb: impl FnMut(u64)) {
for i in 0..64 { cb(self.0 >> i) }
}
}
fn compute(a: &mut A) -> u64 {
let mut acc = 0;
a.gen_data(|x| {
a.change(x); // change
acc ^= a.0; // accumulate result
a.change(x); // undo
});
acc
}
由于实际实现中的条件很多,我无法将
gen_data
更改为迭代器。我不想克隆a
,因为它是一个很大的数据结构,也不想将回调数据累积在容器中,然后进行处理,因为这是热代码。
它在语义上是正确的。有什么方法可以重构这段代码,让 Rust 相信它可以工作吗?
您可以通过使
gen_data
接受 &mut self
并向 &mut Self
添加 FnMut
参数,然后通过该参数而不是通过 a
进行更改来实现此目的:
struct A(u64);
impl A {
fn change(&mut self, x: u64) { self.0 ^= x; }
fn gen_data(&mut self, mut cb: impl FnMut(&mut Self, u64)) {
for i in 0..64 { cb(self, self.0 >> i) }
}
}
fn compute(a: &mut A) -> u64 {
let mut acc = 0;
a.gen_data(|this, x| { // `this` is `a`
this.change(x); // change
acc ^= this.0; // accumulate result
this.change(x); // undo
});
acc
}