asd*_*fle 2 circular-list rust
我正在写一个小型的战略游戏,但我在实现循环链表方面存在问题.
游戏涉及几个人一个接一个地采取行动,直到比赛结束.我可以通过使用循环链表来完成,其中每个元素都是一个引用下一个玩家的玩家.结构是这样的:
#[derive(Debug, Clone)]
struct Player {
name: String,
killed: bool,
next: Option<Box<Player>>,
}
Run Code Online (Sandbox Code Playgroud)
我还想要一个指向当前活动播放器的指针并能够修改它的状态,但我认为Rust不允许我对同一个对象有两个可变引用,因为每个播放器已经有一个对下一个播放器的可变引用.
我想出的是,我可以使用一个简单的可变引用,该引用Box由其之前的播放器拥有并指向当前播放器.我写了一个简单的main函数,出现问题:
fn main() {
let mut p3: Player = Player {
name: "C".to_string(),
killed: false,
next: None,
};
let mut p2: Player = Player {
name: "B".to_string(),
killed: false,
next: Some(unsafe { Box::from_raw(&mut p3) }),
};
let mut p1: Player = Player {
name: "A".to_string(),
killed: false,
next: Some(unsafe { Box::from_raw(&mut p2) }),
};
p3.next = Some(unsafe { Box::from_raw(&mut p1) });
println!("GAME STARTED!");
let mut current_player = p3.next.as_mut().unwrap();
let mut count = 0;
while count < 10 {
println!("Player to kill/save {}", current_player.name);
(*current_player).killed = !(*current_player).killed;
println!(
"Player {} is killed: {}",
(*current_player).name,
(*current_player).killed
);
current_player = (*current_player).next.as_mut().unwrap();
count = count + 1
}
println!("End!");
}
Run Code Online (Sandbox Code Playgroud)
错误也与可变性有关,但我不知道如何解决它.我想知道是否有更好的方法在Rust中实现这个想法,而不是使用循环链表和指向当前播放器的指针.也许我应该切换到另一个结构?
错误消息很长,这里是前几行:
error[E0502]: cannot borrow `current_player` (via `current_player.name`) as immutable because `current_player` is also borrowed as mutable (via `current_player.next`)
--> src/main.rs:29:44
|
29 | println!("Player to kill/save {}", current_player.name);
| ^^^^^^^^^^^^^^^^^^^ immutable borrow of `current_player.name` -- which overlaps with `current_player.next` -- occurs here
...
36 | current_player = (*current_player).next.as_mut().unwrap();
| ---------------------- mutable borrow occurs here (via `current_player.next`)
...
40 | }
| - mutable borrow ends here
Run Code Online (Sandbox Code Playgroud)
如果我改变了as_mut()对方法as_ref()返回的不可改变的参考Box和注释行
// (*current_player).killed = !(*current_player).killed;
Run Code Online (Sandbox Code Playgroud)
程序可以成功构建,但完成后会出现未知的运行时错误.不知道为什么会发生这种情况.
GAME STARTED!
Player to kill/save A
Player A is killed: false
Player to kill/save B
Player B is killed: false
......
Player to kill/save C
Player C is killed: false
Player to kill/save A
Player A is killed: false
End!
error: An unknown error occurred
Run Code Online (Sandbox Code Playgroud)
首先,你应该阅读学习Rust与完全太多链接列表.单链表不简单,不像有多少编程语言对待它们.圆形链表(或双链表)在涉及所有权时非常复杂,这是一个核心的Rust概念.
如果您有循环链表,谁拥有每个项目?这一点很重要,因为值的所有者预计会降低该值.
类似地,由于某种原因,不允许多个可变引用.如果你想要它们,有类似的类型RefCell允许你具有不直接对应于代码结构的可变性.
崩溃的原因就在这里:unsafe.你已经告诉编译器"这很酷,我知道我在做什么",然后你继续打破你所期望的所有保证.如果你想使用unsafe,你应该阅读The Rustonomicon:高级和不安全的Rust编程的黑暗艺术.
但这些都不是真正需要的; 只需使用Box::from_raw:
#[derive(Debug, Clone)]
struct Player {
name: String,
killed: bool,
}
fn main() {
let mut players = vec![
Player {
name: "C".to_string(),
killed: false,
},
Player {
name: "B".to_string(),
killed: false,
},
Player {
name: "A".to_string(),
killed: false,
},
];
println!("GAME STARTED!");
let mut current_player_idx = 0;
let player_count = players.len();
for _ in 0..10 {
let current_player = &mut players[current_player_idx];
println!("Player to kill/save {}", current_player.name);
current_player.killed = !current_player.killed;
println!(
"Player {} is killed: {}",
current_player.name, current_player.killed
);
current_player_idx += 1;
current_player_idx %= player_count;
}
println!("End!");
}
Run Code Online (Sandbox Code Playgroud)
请注意,您不需要任何显式解除引用.
在 Rust 中,&mut不仅意味着可变,而且意味着唯一(与Box<T>\xe2\x80\x93相同T,假定由 唯一拥有Box)。尝试解决它unsafe会违反不变量并导致未定义的行为。你得到的错误就是因为这个(我的猜测是你导致了双重释放(递归?))。如果你想留下来unsafe(不推荐),请坚持*mut在任何地方都使用指针。
内部可变性是您想要做的。您应该熟悉单元模块。这篇关于内部可变性的博文也值得一读。
\n\n所以,我会像这样重新定义你的结构:
\n\nuse std::cell::{Cell,RefCell};\nuse std::rc::Weak;\n\n#[derive(Debug, Clone)]\nstruct Player {\n name: String,\n killed: Cell<bool>,\n next: RefCell<Option<Weak<Player>>>,\n}\nRun Code Online (Sandbox Code Playgroud)\n\n并让所有玩家落后Rc(引用计数指针)。如果您计划让所有玩家仅生活在主函数的堆栈上,
next: Cell<Option<&Player>>,\nRun Code Online (Sandbox Code Playgroud)\n\n应该足够了。
\n\n另一种选择是将整个播放器放入Rc<RefCell<Player>>,但仅将可变部分放入单元格中被认为是很好的做法。