I have a struct instance A that belongs to a struct instance B. I will need to do some processing on this data by calling the parent.
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();
}
When I try to call b.process(), I'm getting the following failure:
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
All of this works fine when mutable references aren't needed.
So my question is, what's the right way to do this when I need this type of relationship between the two structs?
In Rust you can either have a single mutable reference or multiple immutable references, or, put differently: single writer / multiple readers.
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)
Following this logic we can make some observations on the code
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);
}
}
The problem really comes from the fact that A::process(&mut self, b: &mut B)
doesn't know that both self
and b.a
reference the same value when called from B
and expects them to be different. You can make it work by using clones or copies, however it is probably unnecessary.
Personally, I'd probably try to move the logic of process()
entirely to B. In your example A depends on B, but also B depends on A. This would make it so that only B depends on A, which makes things simpler.