借用结构体属性的引用

7

看起来如果你借用了一个结构体字段的引用,整个结构体就会被认为是被借用的。我已经成功地分离出了一个我想要做的事情的示例。我只想得到对B中一个字段的“只读”引用,以获取一些数据,然后修改B的另一个字段。有没有一种符合Rust语言惯例的方法可以实现这个?

struct A {
    i: i32,
}

struct B {
    j: i32,
    a: Box<A>,
}

impl B {
    fn get<'a>(&'a mut self) -> &'a A {
        &*self.a
    }
    fn set(&mut self, j: i32) {
        self.j = j
    }
}

fn foo(a: &A) -> i32 {
    a.i + 1
}

fn main() {
    let a = Box::new(A { i: 47 });
    let mut b = B { a: a, j: 1 };
    let a_ref = b.get();
    b.set(foo(a_ref));
}

error[E0499]: cannot borrow `b` as mutable more than once at a time
  --> src/main.rs:27:5
   |
26 |     let a_ref = b.get();
   |                 - first mutable borrow occurs here
27 |     b.set(foo(a_ref));
   |     ^ second mutable borrow occurs here
28 | }
   | - first borrow ends here
2个回答

6
这是语言的一个特性。从编译器的角度来看,它无法知道在通过get()借用a时调用set()函数是否安全。
您的get()函数可变地借用了b并返回一个引用,因此b将保持借用状态,直到此引用超出作用域。
您有几种处理方式:
  1. Separate your two fields into two different structs

  2. Move the code which needs to access both attribute inside a method of B

  3. Make your attributes public, you will thus be able to directly get references to them

  4. Compute the new value before setting it, like this:

    fn main() {
        let a = Box::new(A { i: 47 });
        let mut b = B { a: a, j: 1 };
        let newval = {
            let a_ref = b.get();
            foo(a_ref)
        };
        b.set(newval);
    }
    

请注意,在“在设置新值之前计算它”中,额外的花括号会导致可变引用的作用域从b.get到期,然后我们再尝试set该值——这与“b将保持借用状态 直到此引用超出作用域为止”的观点密切相关。 - Shepmaster

2

对于Levans' 答案进行一些扩展

将需要访问两个属性的代码移动到B的一个方法中

初步看起来可能是这样的:

impl B {
    fn do_thing(&mut self) {
        self.j = self.a.foo()
    }
}

然而,这样硬编码调用 foo。你也可以接受闭包以使其更加灵活:
impl B {
    fn update_j_with_a<F>(&mut self, f: F)
    where
        F: FnOnce(&mut A) -> i32,
    {
        self.j = f(&mut self.a)
    }
}

// ...

b.update_j_with_a(|a| a.foo())

把两个字段分别放在两个不同的结构体中,这也适用于你借用了两个不相交的属性子集的情况。例如:
struct A {
    description: String,
    name: String,
    age: u8,
    money: i32,
}

impl A {
    fn update_description(&mut self) {
        let description = &mut self.description;
        *description = self.build_description()
        // cannot borrow `*self` as immutable because `self.description` is also borrowed as mutable
    }

    fn build_description(&self) -> String {
        format!(
            "{} is {} years old and has {} money",
            self.name,
            self.age,
            self.money
        )
    }
}

fn main() {}

可以改变为

struct A {
    description: String,
    info: Info,
}

struct Info {
    name: String,
    age: u8,
    money: i32,
}

impl A {
    fn update_description(&mut self) {
        let description = &mut self.description;
        *description = self.info.build_description()
    }
}

impl Info {
    fn build_description(&self) -> String {
        format!(
            "{} is {} years old and has {} money",
            self.name,
            self.age,
            self.money
        )
    }
}

fn main() {}

您可以将这两个步骤结合起来(我认为这是更好的实践),并将该方法移动到内部结构中。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接