我正在为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会可变地借用函数,即使它是借用函数,它也不会在返回结果时返回借位(结束生命周期),所以之后可以借用它?
答案 0 :(得分:13)
编者注:通过引入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
的任何不可变引用,因为它们可能已经(或将来会被)通过可变引用对地图所做的更改失效: / p>
- 在任何给定时间,您可以拥有一个可变引用或任意数量的不可变引用。
- 参考文件必须始终有效。
- The Rust Programming Language chapter on references and borrowing
为什么Rust会可变地借用函数,即使它是借用函数,它也不会在返回结果时返回借位(结束生命周期),所以之后可以借用它?
Rust可变地借用你的 struct ,因为你正在调用一个需要可变引用的方法(&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();
}
另见: