借用对struct中属性的引用

时间:2014-08-26 19:28:55

标签: rust

似乎如果你借用一个struct字段的引用,整个结构被认为是借用的。我设法隔离了我想要做的事情的例子。我只想获得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 个答案:

答案 0 :(得分:4)

这是该语言的一个特色。从编译器的角度来看,没有办法知道在通过set()借用a时调用get()函数是安全的。

您的get()函数可变地借用b并返回引用,因此b将继续借用,直到此引用超出范围。

你有几种处理方法:

  1. 将两个字段分成两个不同的结构

  2. 移动需要在B

  3. 方法中访问这两个属性的代码
  4. 将您的属性公开,您将能够直接获取对它们的引用

  5. 在设置之前计算新值,如下所示:

    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);
    }
    

答案 1 :(得分:2)

Levans' answer

上展开一点
  

移动需要在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() {}

您可以将这两个步骤结合起来(我说它是更好的做法)并将方法移到内部结构上。