Reputation: 1990
I'm trying to borrow a struct mutably along with some of its members in a function call. Seeing the first mutable borrow of the struct, any subsequent borrows (of the members) are marked with the error second mutable borrow occurs here
The scenario is simple, we have a struct that holds the state:
struct State {
a: i32,
b: i32,
result_add: i32,
result_subtract: i32
}
And a function that fills the calculation of result_add
and result_subtract
in the struct based on the passed members (in our case a
& b
)
fn do_calc(state : & mut State, var1 : &mut i32, var2: &mut i32) {
*var1 = 4;
*var2 = 2;
state.result_add = state.a + state.b;
state.result_subtract = state.a - state.b;
}
A bit of a contrived example, but the idea is that var1
and var2
can be other struct members too. Now we call:
do_calc(&mut state, &mut state.a, &mut state.b);
and get the error:
error[E0499]: cannot borrow `state.a` as mutable more than once at a time
--> src/main.rs:24:25
|
24 | do_calc(&mut state, &mut state.a, &mut state.b);
| ------- ---------- ^^^^^^^^^^^^ second mutable borrow occurs here
| | |
| | first mutable borrow occurs here
| first borrow later used by call
I guess the compiler sees that we are borrowing the struct multiple times and stops this, but if the struct as a whole is mutable, shouldn't this be ok?
One solution is to drop the struct reference and borrow every single field that needs to be mutated:
fn do_calc(result_add : &mut i32, result_subtract : &mut i32, var1 : &mut i32, var2: &mut i32)
This works but is cumbersome for something more complicated and quite odd. Is there an alternative to borrowing the struct as a whole in a mutable way while also borrowing its members?
Playground: Original Mentioned solution
Upvotes: 3
Views: 4090
Reputation: 1399
This violates one of the fundamental rules of Rust and borrowing, which is that you can have one mutable reference or many immutable references - but no combination of both.
I guess the compiler sees that we are borrowing the struct multiple times and stops this, but if the struct as a whole is mutable, shouldn't this be ok?
If I borrow the entire struct as mutable, the Rust compiler will ensure that there will be no other reference, mutable or immutable. This is just how Rust and its memory safety works.
There are a number of different ways you could structure this code to make sure there is only one mutable reference - you've already leveraged split borrowing, maybe you could use it further and break your struct up into two smaller structs?
struct Input {
a: i32,
b: i32,
}
struct Output {
add: i32,
subtract: i32,
}
struct State {
input: Input,
result: Result,
}
fn do_calc(input: &mut Input, result: &mut Output) {
input.a = 4;
input.b = 2;
result.add = input.a + input.b;
result.subtract = input.a - input.b;
}
fn main() {
let mut state = State {
input: Input { a: 0, b: 0 },
result: Output {
add: 0,
subtract: 0,
},
};
do_calc(&mut state.input, &mut state.result);
println!("result_add {} ", state.result.add);
println!("result_subtract {} ", state.result.subtract);
}
Upvotes: 2
Reputation: 7937
Maybe your do_calc
has just too much responsibility for a one call. You can split it.
Playground
struct State {
a: i32,
b: i32,
result_add: i32,
result_subtract: i32
}
fn do_calc(var1: &mut i32, var2: &mut i32) {
*var1 = 4;
*var2 = 3;
}
fn finish_calc(state: &mut State){
state.result_add = state.a + state.b;
state.result_subtract = state.a - state.b;
}
fn main() {
let mut state = State { a:0, b:1, result_add:2, result_subtract:3 };
{
do_calc(&mut state.a, &mut state.b);
finish_calc(&mut state);
}
}
Upvotes: -2