考虑以下简单结构:
struct Monster {
// ...
}
struct Player {
// ...
}
struct Missile {
// ...
//target: ???,
}
在编写游戏逻辑时,让对象相互引用是很常见的。在上面的示例中,我们使用结构Monster
,Player
和Missile
来说明所需的交互类型。
想象一下,我们有以下特征:Position
和Think
。以上所有结构都实现了Position
,除了Missile
之外的所有结构都实现了Think
。
首先要注意的是Missile
是一种归航导弹:它存储一个目标,每当游戏更新Missile
个物体时,它就会将它移向它的目标。
据我所知,不可能在Rust中合理地存储此Missile
的目标。
Position
特征。必须通过Rc
或Gc
分享游戏对象。但它不像Missile
只能存储Weak<???>
引用具有Position
特征的内容。 Box<Position>
意味着消耗具有该特征的任何对象。 Box<Any>
不允许向下转换为特征。
制作Missile<T>
并将目标存储为Weak<T>
无济于事。那些Missile<T>
将如何存储?导弹所针对的每种物体都有一个Collection<T>
?游戏对象需要更通用,而可怕的Box<Any>
似乎是不可避免的。
我对Rust很新。令我感到困惑的是,这是不可能的。当然,我一定错过了什么?
答案 0 :(得分:4)
我也是Rust的新手,但这就是我发现的。您可以将std::rc::Rc<T>
结构与Box结合使用。要拥有可变的共享引用,您需要在std::cell::RefCell
。
所以,你的玩家,怪物,导弹示例会是这样的:
use std::cell::RefCell;
use std::rc::Rc;
trait Position {
fn position(&mut self);
}
struct Monster;
impl Position for Monster {
fn position(&mut self) {
println!("Rawr I am getting the monster's position");
}
}
struct Player {
x: i32,
}
impl Position for Player {
fn position(&mut self) {
println!("Getting player's position {}", self.x);
self.x += 1;
}
}
struct Missile {
target: Rc<RefCell<Box<Position>>>,
}
fn main() {
// Create some stuff
let player = Rc::new(RefCell::new(Box::new(Player{x: 42}) as Box<Position>));
let monster = Rc::new(RefCell::new(Box::new(Monster) as Box<Position>));
// Our missile: initial target - monster
let mut missile = Missile{target: monster};
// Should be a monster
missile.target.borrow_mut().position();
// Redirect missile to player
missile.target = player.clone();
// Should be a player
missile.target.borrow_mut().position();
// Show that it is in fact a reference to the original player
player.borrow_mut().position();
}
但是,通常可以设计不需要共享引用的实体系统,这被认为是更惯用的Rust。如果您的实体系统非常复杂,我建议使用实体组件系统。
编辑:改进了代码和信息,并删除了一些不准确的信息。