繁体   English   中英

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

[英]How do I pass a mutable reference to a method on an object that belongs to that reference?

我有一个属于结构实例 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.process() 时,出现以下故障:

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

当不需要可变引用时,所有这些都可以正常工作。

所以我的问题是,当我需要两个结构之间的这种类型的关系时,正确的方法是什么?

你不能这样做违反了 Rusts 别名规则,你将有两个对A可变引用,一个是直接的,一个是通过B间接引用的。 如果你真的想要,你可以使用内部可变性( RefCellMutex ,...)但正如 kaya3 指出的那样,即使从 OO 的角度来看,你建议的实现似乎也很奇怪。 如果它仍然需要B ,我会直接在B上实现它,而无需间接访问。

在 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)不知道selfba在从B调用时引用相同的值并期望它们不同。 您可以通过使用克隆或副本使其工作,但这可能是不必要的。

就个人而言,我可能会尝试将process()的逻辑完全移至 B。在您的示例中,A 依赖于 B,但 B 也依赖于 A。这将使只有 B 依赖于 A,这使事情变得更简单.

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM