I've hit a problem that asked me to read the docs at
rustc --explain E0499
aka "A variable was borrowed as mutable more than once".
But when trying to minimise the problem, I don't understand why my code is failing anymore.
Let's start on one end of the spectrum, with a simple example that does seem to compile. I have a struct with multiple fields inside, and I want to pass those fields to a function that can mutate them.
struct Foo {
a: String,
b: String,
}
pub fn thing_with_foo() {
let mut foo = Foo {
a: "a".to_string(),
b: "b".to_string(),
};
thing_with_strings(&mut foo.a, &mut foo.b)
}
pub fn thing_with_strings(a: &mut String, b: &mut String) {}
Unless that's doing some magic copying, that looks like the compiler actually understands that the two fields are being independently mutated and that the foo
is doesn't have a double mutation leak.
But now let's say we're accessing Foo
from an async_rwlock::RwLock
instead (presumably the same will apply for the sync
version)
pub async fn thing_with_foo() {
let foo = Foo {
a: "a".to_string(),
b: "b".to_string(),
};
let locked = RwLock::new(foo);
{
let mut bar = locked.write().await;
thing_with_strings(&mut bar.a, &mut bar.b);
}
}
now my compile fails with
37 | thing_with_strings(&mut bar.a, &mut bar.b);
| ------------------ --- ^^^ second mutable borrow occurs here
| | |
| | first mutable borrow occurs here
| first borrow later used by call
have I done something horribly wrong here?
The RwLock
is dropped at the end of the block. I added an explicit block here to make that clear. bar
is actually a RwLockWriteGuard<'_, Foo>
although it mostly behaves like a Foo
. I tried shadowing the bar
variable here (which doesn't cause the lock to be dropped, because shadowing doesn't remove it from the scope) to remove the complexity introduced by the guard forwarding the methods but I wasn't able to figure out how to do that.
Also I tried splitting out the fields into separate variables, but that also fails
let mut a = &mut bar.a;
let mut b = &mut bar.b;
36 | let mut a = &mut bar.a;
| --- first mutable borrow occurs here
37 | let mut b = &mut bar.b;
| ^^^ second mutable borrow occurs here
I'm not trying to borrow the struct twice, I just want to split up the contents of that struct into two separately mutable parts... I'm really confused why I can do that in my simple example but not when I introduce the RwLock into the equation.