Editor's note: This specific problem has been solved by the introduction of non-lexical lifetimes.
Let's look at a tiny reproduction:
struct Site {
owner: u8,
}
struct GameMap {
site: Site,
}
impl GameMap {
fn do_anything(&self) {}
fn get_site(&mut self) -> &mut Site {
&mut self.site
}
}
fn main() {
let mut game_map = GameMap {
site: Site { owner: 0 },
};
let site = game_map.get_site();
game_map.do_anything();
}
error[E0502]: cannot borrow `game_map` as immutable because it is also borrowed as mutable
--> src/main.rs:22:5
|
21 | let site = game_map.get_site();
| -------- mutable borrow occurs here
22 | game_map.do_anything(); // Compiler error!
| ^^^^^^^^ immutable borrow occurs here
23 | }
| - mutable borrow ends here
Our GameMap
only owns a single Site
, but that's enough. The call to get_site
returns a reference (in this case it happens to be mutable):
fn get_site(&mut self) -> &mut Site
Thanks to lifetime elision, this is the same as
fn get_site<'a>(&'a mut self) -> &'a mut Site
This means that the returned reference is allowed to point to something inside of GameMap
(which it does). Then we keep that reference in a variable - site
!
That means that we can no longer use any immutable references to game_map
as they might have been (or will in the future be) invalidated by the changes that can be made to the map through the mutable reference:
- At any given time, you can have either one mutable reference or any number of immutable references.
- References must always be valid.
— The Rust Programming Language chapter on references and borrowing
Why does Rust borrow the function mutably, and even if it is borrowing the function would it not return the borrow (ending the lifetime) when returning the result, so it would be available to borrow afterwards?
Rust borrows your struct mutably because you are calling a method that requires a mutable reference (&mut self
). That method then returns a mutable reference, transferring the borrow of the struct to the returned value. The borrow ends when the returned value goes out of scope.
So, how do you fix it? Probably the most flexible solution is to introduce a scope to constrain the mutable borrow:
let zhu_li_do_the_thing = {
let site = game_map.get_site();
site.owner == 5 || site.owner == 42
};
if zhu_li_do_the_thing {
game_map.do_anything();
}
Another is the same idea, but requires that you never store the borrow in a variable at all. Thus the mutable borrow doesn't last beyond that statement:
if game_map.get_site().owner == 42 {
game_map.do_anything();
}
It's common for idiomatic Rust code to have foo
and foo_mut
variants of a method, for when you don't need mutability. This may not help if you need to mutate game_map
while the immutable borrow of site
is still outstanding.
fn get_site(&self) -> &Site {
&self.site
}
fn get_site_mut(&mut self) -> &mut Site {
&mut self.site
}
let site = game_map.get_site();
if site.owner == 5 || site.owner == 42 {
game_map.do_anything();
}
See also: