我正在为halite.io编写一个机器人,并且在理解借用的一些影响方面遇到了问题。这是无法编译的代码:
let scanLoc = hlt::types::Location {
x: oflow(coord.0 + l.x as i32, game_map.width),
y: oflow(coord.1 + l.y as i32, game_map.width),
};
let scan = game_map.get_site(scanLoc, types::STILL);
if (&scan.owner != id) | (scan.owner != 0u8) {
let ang = game_map.get_angle(l, scanLoc);
debug!("angle b/w: {}", ang);
return (l, 2);
}
这是编译器错误:
error[E0502]: cannot borrow `*game_map` as immutable because it is also borrowed as mutable
--> src/MyBot.rs:112:27
|
110 | let scan = game_map.get_site(scanLoc, types::STILL);
| -------- mutable borrow occurs here
111 | if (&scan.owner != id) | (scan.owner != 0u8) {
112 | let ang = game_map.get_angle(l, scanLoc);
| ^^^^^^^^ immutable borrow occurs here
...
116 | }
| - mutable borrow ends here
这是GameMap
函数和结构的代码:
#[derive(Clone, Debug)]
pub struct GameMap {
pub width: u16, // Number of columns.
pub height: u16, // Number of rows.
pub contents: Vec<Vec<Site>>,
}
impl GameMap {
pub fn in_bounds(&self, l: Location) -> bool {
// ...
}
pub fn get_distance(&self, l1: Location, l2: Location) -> u16 {
// ...
}
pub fn get_angle(&self, l1: Location, l2: Location) -> f64 {
// ...
}
pub fn get_location(&self, l: Location, d: u8) -> Location {
// ...
}
pub fn get_site(&mut self, l: Location, d: u8) -> &mut Site {
// ...
}
}
为什么Rust可以相互借用函数,即使它是借用函数,它也不会在返回结果时返回借位(结束生命周期),所以之后可以借用它?
编者注:这个具体问题已经通过引入non-lexical lifetimes解决了。
让我们来看一个微小的再现:
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
我们的GameMap
只拥有一个Site
,但这就足够了。对get_site
的调用返回一个引用(在这种情况下,它恰好是可变的):
fn get_site(&mut self) -> &mut Site
感谢lifetime elision,这是一样的
fn get_site<'a>(&'a mut self) -> &'a mut Site
这意味着允许返回的引用指向GameMap
内部的内容(它确实如此)。然后我们将该引用保存在变量中 - site
!
这意味着我们不能再使用对game_map
的任何不可变引用,因为它们可能已经(或将来会被)通过可变引用对地图所做的更改失效:
- 在任何给定时间,您可以拥有一个可变引用或任意数量的不可变引用。
- 引用必须始终有效。
- The Rust Programming Language chapter on references and borrowing
为什么Rust可以相互借用函数,即使它是借用函数,它也不会在返回结果时返回借位(结束生命周期),所以之后可以借用它?
Rust可变地借用你的结构,因为你正在调用一个需要可变引用的方法(&mut self
)。然后该方法返回一个可变引用,将结构的借位转移到返回的值。当返回的值超出范围时,借用结束。
那么,你如何解决它?可能最灵活的解决方案是引入限制可变借用的范围:
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();
}
另一个是相同的想法,但要求你永远不要将借入存储在一个变量中。因此,可变借款不会超出该陈述:
if game_map.get_site().owner == 42 {
game_map.do_anything();
}
对于惯用的Rust代码来说,有一个方法的foo
和foo_mut
变体是常见的,因为当你不需要可变性时。如果你需要改变game_map
而site
的不可变借款尚未兑现,这可能无济于事。
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();
}
也可以看看: