我被困在借用检查器上。
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 来写这个?
错误措辞有点糟糕,但我看到了你的问题。
该错误表示不可变借用发生在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
本身也必须"隐式"借用不变。
换句话说,game_loop
中pong
的借用持续如下:
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.
这就解释了原因。至于如何解决它:从理论上讲,最简单的解决方案是在Gamepad
上实现Clone
(如果Gamepad
的所有字段都实现clone
,这可以很容易地用#[derive(Clone)]
完成;标准实现基本上是通过在原始字段的所有字段上调用.clone
来创建一个新对象),然后使用gp.clone()
而不仅仅是gp
。
这对程序的内存使用有(可能可以忽略不计)的影响,但此外,如果Gamepad
使用不实现Clone
的外部库类型,则可能不可行 - 您无法在这些外部类型上实现Clone
,因为您没有在项目中定义Clone
或SomeExternalType
。
如果impl Clone
不可用,您可能需要重构代码;重新考虑为什么需要某些可变或不可变的借用,并在不必要的情况下删除它们。如果失败,您可能需要查看其他类型的指针,例如Cell
,我没有资格提供相关信息!
如果你不需要在调用update_game
后保持vec
并使用它执行操作,请考虑以下解决方案:
fn game_loop(pong: &mut Pong) {
{
let mut vec: Vec<&Gamepad> = Vec::new(); // <+ Vec is created
for playerdata in pong.players.iter() { // |
match playerdata.player { // |
Player::Human(ref gp) => { // |
if gp.str == "mydev" { // |
vec.push(gp); // |
} // |
} // |
_ => {} // |
} // |
} // |
for g_pad in vec { // |
// Do something with each gamepad // |
} // |
} // <- `vec` is dropped
// Since `vec` no longer exists, there are no more references
// to the contents of `pong`, and `update_game` can be called.
update_game(pong);
}
希望这有帮助。