似乎如果你借用一个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
答案 0 :(得分:4)
这是该语言的一个特色。从编译器的角度来看,没有办法知道在通过set()
借用a
时调用get()
函数是安全的。
您的get()
函数可变地借用b
并返回引用,因此b
将继续借用,直到此引用超出范围。
你有几种处理方法:
将两个字段分成两个不同的结构
移动需要在B
将您的属性公开,您将能够直接获取对它们的引用
在设置之前计算新值,如下所示:
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)
移动需要在
方法中访问这两个属性的代码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() {}
您可以将这两个步骤结合起来(我说它是更好的做法)并将方法移到内部结构上。