rust 如何将可变引用传递给属于该引用的对象上的方法?

pkln4tw6  于 2022-12-23  发布在  其他
关注(0)|答案(2)|浏览(167)

我有一个属于结构示例B的结构示例A。我需要通过调用父示例对该数据进行一些处理。

struct A {
    val : u8
}

struct B {
    data : u8,
    a : A
}

impl A {
    pub fn process(&mut self, b : &mut B) {
        println!("Processing {} - {}", b.data, self.val);
        //do stuff with val and data...
    }
}

impl B {
    pub fn process(&mut self) {
        self.a.process(self);
    }
}

fn main() {
    let mut b = B {
        data : 0,
        a : A {
            val : 3
        }
    };

    b.process();
}

当我尝试调用B()时,我遇到了以下失败:

error[E0499]: cannot borrow `self.a` as mutable more than once at a time
  --> src/main.rs:47:9
   |
47 |         self.a.process(self);
   |         ^^^^^^^-------^----^
   |         |      |       |
   |         |      |       first mutable borrow occurs here
   |         |      first borrow later used by call
   |         second mutable borrow occurs here

error[E0499]: cannot borrow `*self` as mutable more than once at a time
  --> src/main.rs:47:24
   |
47 |         self.a.process(self);
   |         ---------------^^^^-
   |         |      |       |
   |         |      |       second mutable borrow occurs here
   |         |      first borrow later used by call
   |         first mutable borrow occurs here

当不需要可变引用时,所有这些都可以正常工作。
所以我的问题是,当我需要两个结构体之间的这种关系时,正确的方法是什么?

rdrgkggo

rdrgkggo1#

这违反了Rusts的别名规则,您将有两个对A的可变引用,一个是直接引用,另一个是通过B间接引用。(RefCellMutex,...)但是正如kaya3所指出的,即使从OO的Angular 来看,您建议的实现也显得相当奇怪。我只是直接在B上实现它,而不使用间接方法。

yfwxisqw

yfwxisqw2#

在Rust中,你可以有一个可变引用或者多个不变引用,或者,换句话说:单个写入器/多个读取器。

let mut a = 0; // Only mutable variables can be written to
    let a1 = &a;
    let a2 = &a; // Readers can be many
    // This is an error:
    let a3 = &mut a; // You cannot write to `a` while it is borrowed for reading

    print!("{} {} {}", a1, a2, a3)
let mut a = 0;
    let a1 = &mut a; // You can have a single writer

    // This is an error:
    let a2 = &mut a; // But there can be only one
    // This is also an error:
    let a3 = &a; // You cannot read while `a` can be written to

    print!("{} {} {}", a1, a2, a3)

遵循此逻辑,我们可以对代码进行一些观察

impl A {
    // this implies that both self and B can change
    pub fn process(&mut self, b: &mut B) {
        println!("Processing {} - {}", b.data, self.val);
        //do stuff with val and data...
    }

    pub fn process_changes_a(&mut self, b: &B) {
        println!("Processing {} - {}", b.data, self.val);
    }

    pub fn process_changes_b(&self, b: &mut B) {
        println!("Processing {} - {}", b.data, self.val);
    }
}

impl B {
    pub fn process(&mut self) {
        // it's possible to use clones to work around this
        // however it's probably better to refactor the logic

        // let's see what happens when using clones
        let mut tmp_self = self.clone();
        let mut tmp_a = self.a.clone();

        // this can modify both self.a and tmp_self
        self.a.process(&mut tmp_self);
        // now self and tmp_self could be different

        // does self need to have the value of the &mut B?
        // note: this clone is only necessary for
        // the other examples to compile
        *self = tmp_self.clone();

        // or does only self.a need to change?
        tmp_a.process(self);
        self.a = tmp_a;

        // or should the original self.a stay the same?
        self.data = tmp_self.data;
    }

    pub fn process_changes_a(&mut self) {
        // you still need a clone of self.a
        // otherwise A::process_changes_a could
        // modify a through self while reading b.a
        let mut tmp_a = self.a.clone();
        tmp_a.process_changes_a(self);
        self.a = tmp_a;
    }

    pub fn process_changes_b(&mut self) {
        // you still need a clone of self.a
        // otherwise A::process_changes_b could
        // modify a through b.a while reading self
        let tmp_a = self.a.clone();
        tmp_a.process_changes_b(self);
    }

    pub fn process_on_self(&mut self) {
        // if you need to modify both self and self.a
        // it might be best to have the method directly on B
        println!("Processing {} - {}", self.data, self.a.val);
    }
}

真正的问题在于A::process(&mut self, b: &mut B)不知道selfb.a在从B调用时引用了相同的值,并期望它们是不同的。您可以通过使用克隆或副本来使其工作,但这可能是不必要的。
就我个人而言,我可能会尝试将process()的逻辑完全移到B上。在您的示例中,A依赖于B,但B也依赖于A。这将使只有B依赖于A,从而使事情变得更简单。

相关问题