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

问题描述 投票:0回答:1

我有一个通过回调生成数据的结构。在回调内部,我需要修改结构、进程,然后取消修改。在回调结束时,该结构在语义上未修改,但代码具有 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 相信它可以工作吗?

rust borrow-checker
1个回答
0
投票

您可以通过使

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
}
© www.soinside.com 2019 - 2024. All rights reserved.