考虑以下简单结构:
struct Monster {
// ...
}
struct Player {
// ...
}
struct Missile {
// ...
//target: ???,
}
Run Code Online (Sandbox Code Playgroud)
在编写游戏逻辑时,让对象相互引用是很常见的.在上面的例子中,我们有结构Monster,Player并Missile说明所需的各种交互.
想象一下,我们有以下特点:Position和Think.以上所有结构都实现了Position,除了Missile实现之外的所有结构Think.
首先要注意的是它Missile是一个归航导弹:它存储一个目标,每当游戏更新Missile物体时,它就会将它移向它的目标.
据我所知,不可能Missile在Rust中合理地存储这个目标.
显然,导弹没有自己的目标.它只是想要访问它的Position特性.游戏对象必须通过Rc或共享Gc.但它并不像Missile只能存储Weak<???>具有Position特征的东西的引用.Box<Position>消耗具有该特征的任何对象的手段.A Box<Any>不允许向下转向特征.
制作Missile<T>和存储目标Weak<T>无济于事.怎么Missile<T>存储?在Collection<T>导弹所针对的每种物体中都有一种?游戏对象需要更通用,而可怕的游戏Box<Any>似乎是不可避免的.
我对Rust很新.令我感到困惑的是,这是不可能的.当然,我一定错过了什么?
我对 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();
}
Run Code Online (Sandbox Code Playgroud)
然而,通常可以设计不需要共享引用的实体系统,这被认为是更惯用的 Rust。如果您的实体系统非常复杂,我建议使用实体组件系统。
编辑:改进了代码和信息,并删除了一些不准确的信息。