rust 如何用回调构造语义正确的递归可变借位以满足借位检查器?

brjng4g3  于 11个月前  发布在  其他
关注(0)|答案(1)|浏览(102)

我有一个通过回调生成数据的结构体。在回调中,我需要修改结构体,处理,然后取消修改。在回调结束时,结构体在语义上没有修改,但代码有一个递归的可变借用,这是Rust不喜欢的。
下面是一个简化的example

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相信它可以工作?

9gm1akwq

9gm1akwq1#

你可以让gen_data使用&mut self,并向FnMut添加一个&mut Self参数,然后通过该参数而不是通过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` will be `a` when `cb` is called
        this.change(x); // change
        acc ^= this.0;  // accumulate result
        this.change(x); // undo
    });
    acc
}

字符串

相关问题