我被困在借阅检查器上。
pub struct Gamepad {
str: String,
}
pub enum Player {
Human(Gamepad),
Computer,
}
pub struct PlayerData {
pub player: Player, /* actually this should be private */
}
struct Pong {
players: Vec<PlayerData>,
}
fn update_game(_pong: &mut Pong) {}
fn main() {
println!("Hello, world!");
let mut pong = Pong {
players: vec![
PlayerData {
player: Player::Computer,
},
PlayerData {
player: Player::Human(Gamepad {
str: "mydev".to_string(),
}),
},
],
};
game_loop(&mut pong);
}
fn game_loop(pong: &mut Pong) {
let mut vec: Vec<&Gamepad> = Vec::new();
{
for playerdata in pong.players.iter() {
match playerdata.player {
Player::Human(ref gp) => {
if gp.str == "mydev" {
vec.push(gp); //omitting this line of code fixes borrow checker issues
}
}
_ => {}
}
}
}
update_game(pong);
}
这给出了:
error[E0502]: cannot borrow `*pong` as mutable because `pong.players` is also borrowed as immutable
--> src/main.rs:52:17
|
41 | for playerdata in pong.players.iter() {
| ------------ immutable borrow occurs here
...
52 | update_game(pong);
| ^^^^ mutable borrow occurs here
53 | }
| - immutable borrow ends here
虽然我可以在某种程度上理解错误,但是来自C和Java背景,我真的很难摆脱这个问题。我很困惑为什么在for循环结束后不会释放不可变借用。你会怎么用惯用的Rust写这个?
答案 0 :(得分:1)
错误措辞有点差,但我看到了你的问题。
错误说不可变借用发生在for
循环中,这不太正确。相反,它出现在您标记的行上:vec.push(gp)
。
gp
是 pong.players
中包含的对象的不可变引用。退出循环时,没有pong.players
本身的不可变引用,但 是一个向量,其中包含对该向量内对象的引用。
pong.players : [ a, b, c, d, e]
^ ^ ^ ^ ^
vec : [&a, &b, &c, &d, &e]
由于您对pong.players
内的对象有明确的不可变引用,因此Rust必须将pong.players
视为“隐式地”不可变地借用,以确保在仍有不可变引用的情况下不会突变其内容到那个项目。由于pong.players
是pong
的一个组成部分并且是“隐式”借用的,因此pong
本身也必须“隐式”借用。
换句话说,pong
中game_loop
的借用持续如下:
fn game_loop(pong: &mut Pong) {
let mut vec: Vec<&Gamepad> = Vec::new(); // <+ `vec`'s lifetime begins here
{ // |
for playerdata in pong.players.iter() { // <+ `pong.players.iter()` temporarily immutably borrows
// | `players` from `pong` for the iterator. `playerdata`
// | is a borrowed portion of `pong.players`.
// | As long as any `playerdata` exists, `pong.players`
// | is immutably borrowed by extension.
match playerdata.player { // <+ `playerdata.player` is a portion of `playerdata`.
Player::Human(ref gp) => { // <+ `gp` is a borrow of an element of `playerdata`.
if gp.str == "mydev" { // |
vec.push(gp); // <! At this point, `gp` is added to `vec`.
// | Since `gp` is inside `vec`, the reference to `gp`
// | is not dropped *until `vec` is dropped.
} // |
} // <- `gp`'s *lexical* lifetime ends here, but it may still
// | be inside `vec`. Any `gp` added to `vec` is still
// | considered borrowed.
_ => {} // |
} // <- `playerdata.player` is not longer lexically borrowed.
// | However, since `gp`, a portion of `playerdata.player`,
// | may still be borrowed, the compiler flags
// | `playerdata.player` as still borrowed.
} // <- `playerdata`'s borrow scope ends here, but since
// | `playerdata.player` may still be borrowed (due to the
// | fact that `vec` may contain references to elements of
// | playerdata.player), `playerdata` is still considered borrowed
} // <- the iterator over `pong.players` is dropped here. But since
// | `playerdata` might still be referenced in `vec`, `pong.players`
// | is still considered borrowed... and since `pong.players` is
// | implicitly borrowed, `pong` is implicitly borrowed.
update_game(pong); // <! When you reach this line, `pong` is implicitly borrowed because
// | there are references to something 'inside' it. Since you can't
// | have an immutable borrow and a mutable borrow at the same time
// | (to ensure you can't change something at the same time another
// | part of the program views it), `update_game(pong)` cannot accept
// | a mutable reference to `pong`.
} // <- At this point, `vec` is dropped, releasing all references to the
// | contents of `pong`. `pong` is also dropped here, because it is the
// | end of the function.
这解释了为什么。至于如何解决它:从理论上讲,最简单的解决方案是在Clone
上实现Gamepad
(可以使用{轻松完成{1}}如果所有#[derive(Clone)]
的字段都实现Gamepad
;标准实现基本上是通过在原始字段的所有字段上调用clone
来创建新对象,然后使用.clone
而非gp.clone()
。
这对程序的内存使用有一个(可能是微不足道的)影响,但是,如果gp
使用未实现Gamepad
的外部库类型,那么它是不可行的 - 你可以'在这些外部类型上实施Clone
,因为您没有在项目中定义Clone
或Clone
。
如果您无法使用SomeExternalType
,则可能需要重构代码;重新考虑为什么你需要某些可变或不可变的借用,如果没有必要则删除它们。如果失败了,您可能需要查看其他类型的指针,例如impl Clone
,我没资格提供相关信息!
如果您在调用Cell
后不需要保留vec
并执行操作,请考虑以下解决方案:
update_game
希望这有帮助。