Consider the following simple structs:
struct Monster {
// ...
}
struct Player {
// ...
}
struct Missile {
// ...
//target: ???,
}
When writing game logic, it is very common to have objects refer to each other. In the example above, we have the structs Monster, Player and Missile to illustrate the kinds of interactions needed.
Imagine we have the following traits: Position and Think. All the above structs implements Position, and all except Missile implements Think.
The first thing to note is that Missile is a homing missile: It stores a target and whenever the game updates Missile objects, it will move it toward it's target.
As far as I can tell, it is impossible to sensibly store the target of this Missile in Rust.
Obviously, the missile doesn't own its target. It just wants to access its Position trait. The game objects must be shared, through Rc or Gc. But it's not like the Missile can just store a Weak<???> reference to something with a Position trait. A Box<Position> means consuming whatever object had that trait. A Box<Any> does not allow downcasting to traits.
Making Missile<T> and storing the target as Weak<T> doesn't help. How would those Missile<T> be stored? In one Collection<T> for each kind of object the missile targets? Game objects will need to be more generic, and the dreadful Box<Any> seems inevitable.
I'm rather new to Rust. It baffles my mind that this is straight out impossible. Surely, I must be missing something?
I'm also pretty new to Rust, but here's what I've found. You can use the std::rc::Rc<T> struct in combination with Box. To have a mutable shared references, you need to wrap boxed item in a std::cell::RefCell.
So, your player, monster, missile example would go something like this:
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();
}
However, it is usually possible to design entity systems that don't require shared references, and this is considered to be more idiomatic Rust. If your entity system is really complex, I'd recommend using an Entity Component System.
EDIT: Improved code and information, and removed some inaccurate information.
Related
I have a Struct Enemy as well as a Struct Turret. The goal is once a turret spots an enemy, It should keep track of it, attacking it until it is either dead or out of sight.
I have a query
fn update_turrets(mut enemy_query: Query<(&mut Enemy, Entity)>, mut turret_query: Query<(&mut Turret)>) {
...
}
where I can find possible targets for a Turret. As mentioned, once a target has been found, the tower should lock it. Coming from C++ I would have stored a pointer/reference of the Enemy as member in the Turret Struct, thus i tried doing that
pub struct Turret {
pub target: *mut Enemy,
}
This gives me the following error: *mut Enemy cannot be sent between threads safely which makes sense to me. Furthermore it looks like there is no way to "ignore" that as bevy seems to demands it.
Afterwards I tried to save the entity id as u32 as member in Turret instead and building a Hashmap HashMap<u32, *mut Enemy> at the beginning of each iteration but this seems to be very inefficient because I would have to rebuild that hashmap on each iteration.
What is the "rust-way" of solving this efficiently?
Don't try to keep a reference to a component. Keep a handle on the entity:
pub struct Turret {
pub target: Option<Entity>,
}
Then, if you need to access the Enemy component, you can access it by passing the entity handle to .get() on an enemy query.
I'm trying to write a simple game engine, but the normal patterns I would use to do this don't work here due to the strict borrowing rules.
There is a World struct which owns a collection of Object trait objects. We can think of these as moving physical objects in the game engine. World is responsible for calling update and draw on each of these during each game tick.
The World struct also owns a collection of Event trait objects. Each of these events encapsulates an arbitrary piece of code that modifies the objects during the game ticks.
Ideally, the Event trait object has a single method do_event that takes no arguments that can be called by World during each game tick. The problem is that a particular Event needs to have mutable references to the objects that it modifies but these objects are owned by World. We could have World pass mutable references to the necessary objects when it calls do_event for each event, but how does it know which objects to pass to each event object?
If this were C++ or Python, when constructing a particular Event, I would pass the target of the event to the constructor where it would be saved and then used during do_event. Obviously, this doesn't work in Rust because the targets are all owned by World so we cannot store mutable references elsewhere.
I could add some ID system and a lookup table so World knows which objects to pass to which Events but this is messy and costly. I suspect my entire way of approaching this class of problem needs to change, however, I am stumped.
A sketch of my code:
trait Object {
fn update(&mut self);
fn draw(&self);
}
trait Event {
fn do_event(&mut self);
}
struct World {
objects: Vec<Box<dyn Object + 'a>>,
events: Vec<Box<dyn Event + 'a>>,
}
impl World {
fn update(&mut self) {
for obj in self.objects.iter_mut() {
obj.update();
}
for evt in self.events.iter_mut() {
evt.do_event();
}
}
fn draw(&mut self) { /*...*/ }
}
struct SomeEvent<'a, T: Object> {
target: &'a mut T,
}
impl<'a, T: Object> Event for SomeEvent<'a, T> {
fn update(&self) {
self.target.do_shrink(); // need mutable reference here
}
}
I know that RefCell enables multiple objects to obtain mutable references. Perhaps that is the direction I should go. However, based on what I learned from the Rust book, I got the sense that RefCells break some of Rust's main ideas by introducing unsafe code and circular references. I guess I was just wondering if there was some other obvious design pattern that adhered more to the idiomatic way of doing things.
After reading the question comments and watching kyren's RustConf 2018 talk (thanks trentcl), I've realized the OO approach to my game engine is fundamentally incompatible with Rust (or at least quite difficult).
After working on this for a day or so, I would highly recommend watching the posted video and then using the specs library (an implementation of the ECS system mentioned by user2722968).
Hope this helps someone else in the future.
There is a new Pin type in unstable Rust and the RFC is already merged. It is said to be kind of a game changer when it comes to passing references, but I am not sure how and when one should use it.
Can anyone explain it in layman's terms?
What is pinning ?
In programming, pinning X means instructing X not to move.
For example:
Pinning a thread to a CPU core, to ensure it always executes on the same CPU,
Pinning an object in memory, to prevent a Garbage Collector to move it (in C# for example).
What is the Pin type about?
The Pin type's purpose is to pin an object in memory.
It enables taking the address of an object and having a guarantee that this address will remain valid for as long as the instance of Pin is alive.
What are the usecases?
The primary usecase, for which it was developed, is supporting Generators.
The idea of generators is to write a simple function, with yield, and have the compiler automatically translate this function into a state machine. The state that the generator carries around is the "stack" variables that need to be preserved from one invocation to another.
The key difficulty of Generators that Pin is designed to fix is that Generators may end up storing a reference to one of their own data members (after all, you can create references to stack values) or a reference to an object ultimately owned by their own data members (for example, a &T obtained from a Box<T>).
This is a subcase of self-referential structs, which until now required custom libraries (and lots of unsafe). The problem of self-referential structs is that if the struct move, the reference it contains still points to the old memory.
Pin apparently solves this years-old issue of Rust. As a library type. It creates the extra guarantee that as long as Pin exist the pinned value cannot be moved.
The usage, therefore, is to first create the struct you need, return it/move it at will, and then when you are satisfied with its place in memory initialize the pinned references.
One of the possible uses of the Pin type is self-referencing objects; an article by ralfj provides an example of a SelfReferential struct which would be very complicated without it:
use std::ptr;
use std::pin::Pin;
use std::marker::PhantomPinned;
struct SelfReferential {
data: i32,
self_ref: *const i32,
_pin: PhantomPinned,
}
impl SelfReferential {
fn new() -> SelfReferential {
SelfReferential { data: 42, self_ref: ptr::null(), _pin: PhantomPinned }
}
fn init(self: Pin<&mut Self>) {
let this : &mut Self = unsafe { self.get_unchecked_mut() };
// Set up self_ref to point to this.data.
this.self_ref = &mut this.data as *const i32;
}
fn read_ref(self: Pin<&Self>) -> Option<i32> {
let this : &Self= self.get_ref();
// Dereference self_ref if it is non-NULL.
if this.self_ref == ptr::null() {
None
} else {
Some(unsafe { *this.self_ref })
}
}
}
fn main() {
let mut data: Pin<Box<SelfReferential>> = Box::new(SelfReferential::new()).into();
data.as_mut().init();
println!("{:?}", data.as_ref().read_ref()); // prints Some(42)
}
I am very interested in Rust and am now starting my first non-trivial project in the language. I am still having a bit of trouble fully understanding the concepts of borrowing and lifetime.
The application is a logic gate simulator in which components are defined recursively (in terms of other components and their interconnections).
My current plan is to implement this similarly as I would in C++ by having a Component structure owning a vector of Components (its sub-components) and a vector of Nets describing the inter-connections between those components:
pub struct Pin {
name: String
}
pub struct Net<'a> {
nodes: Vec<(&'a Component<'a>,&'a Pin)>
}
pub struct Component<'a> {
sub_components: Vec<Box<Component<'a>>>,
in_pins: Vec<Pin>,
out_pins: Vec<Pin>,
netlist: Vec<Net<'a>>
}
impl<'a> Component<'a> {
pub fn new() -> Component<'a> {
...
}
pub fn add_subcomponent( & mut self, comp: Component<'a> ) {
// -> &Box<Component<'a>> ??
....
}
}
In C++, Net would be easy to implement as an array of pointers to Components but I am not sure of the best way to do this in Rust, I suppose I should use borrowed pointers? Or is there a better way?
Consider the following main:
fn main() {
let sub1 = Component::new();
let sub2 = Component::new();
let circuit = Component::new();
circuit.add_subcomponent( sub1 );
circuit.add_subcomponent( sub2 );
// sub1 and sub2 are now empty...
}
How can I configure circuit to create a net between sub1 and sub2? Shall I have add_subcomponent returns a borrowed pointer to the added Component? or the Box?
It would be great if someone could point me in the right direction.
Thanks a lot.
You can't represent an arbitrary graph structure in safe rust.
The best way to implement this pattern is to use unsafe code and raw pointers, or an existing abstraction that wraps this functionality in a safe api, for example http://static.rust-lang.org/doc/master/std/cell/struct.RefCell.html
For example, the typical bi directional linked list would be:
struct Node {
next: Option<Node>, // Each node 'owns' the next one
prev: *mut Node // Backrefs are unsafe
}
There have been a number of 'safe' implementations floating around, where you have something like:
struct Node {
id: u32,
next: u32,
prev: u32
}
struct Nodes {
all:Vec<Node>,
root:Option<Node>
}
This is 'technically' safe, but it's a terrible pattern; it breaks all the safety rules by just manually implementing raw pointers. I strongly advise against it.
You could try using references, eg:
struct Container<'a> {
edges:Vec<Edge<'a>>,
pub root: Node
}
struct Node {
children:Vec<Node>
}
struct Edge<'a> {
n1: &'a Node,
n2: &'a Node
}
...but you'll stumble almost immediately into borrow checker hell. For example, when you remove a node, how does the borrow checker know that the associated links in 'edges' are no longer valid?
Although you might be able to define the structures, populating them will be extremely troublesome.
I know that's probably a quite unsatisfying answer; you may find it useful to search github for 'rust graph' and 'rust tree' and look at the implementations other people have done.
Typically they enforce single-ownership of a sub-tree to a parent object.
Note: This was asked about a pre-1.0 version of Rust, involving a feature which has since been withdrawn from the language.
I am using the experimental feature feature(struct_inherit).
I have the LocalPlayer and NetPlayer structs and they both implement the Inputable trait as well as inherit the player_num field from the virtual struct Player. Depending on how a game starts, player_2 in my program can either be a LocalPlayer or NetPlayer. Depending on which one, the way that the Inputable trait is implemented changes.
The compiler won't let me dynamically assign player_2 a type depending on whether it's a NetPlayer or LocalPlayer. It complains:
error: mismatched types: expected ~player::LocalPlayer but found ~player::NetPlayer (expected struct player::LocalPlayer but found struct player::NetPlayer)
It also won't let me typecast NetPlayer or LocalPlayer pointers to a Player pointer. It claims they aren't scalable.
The code sample in question is as follows:
let player1 = box player::LocalPlayer::new(0);
let player2;
if local {
player2 = box player::LocalPlayer::new(1);
} else if hosting {
player2 = box player::NetPlayer::new(1);
player2.host();
} else {
player2 = box player::NetPlayer::new(1);
player2.connect();
}
/* ... Omitted Code ... */
let input = player2.get_input(); // Trait function
The struct implementations are as follows:
pub virtual struct Player {
player_num: uint
}
pub struct LocalPlayer: Player;
pub struct NetPlayer: Player;
Struct inheritance in Rust is very primitive; you probably don't want to use it. There is no subtyping or coercion between inheriting structs in Rust. The feature basically just allows you to save typing if you have a lot of structs with similar fields (it's also future proofing for maybe adding more features around them in the future).
You could make Player, LocalPlayer and NetPlayer traits. You then get the subtyping behaviour you want between them. Alternatively, you could make just LocalPlayer and NetPlayer structs and let Player be a trait. You could even have a PlayerImpl struct which you could inherit from the way you do currently (if you have a lot of fields to share), but you would need to write independent impls for both structs.
I ended up implementing Player as an enum:
pub enum Player {
Net(NetPlayer),
Local(LocalPlayer)
}
Every time I call a shared function, I need to do the following:
let input = match player2 {
player::Net(player) => player.get_input(),
player::Local(player) => player.get_input(),
};
Rust's traits are similar to interfaces, and can be composed to emulate a hierarchy of interfaces. They're often an alternative to inheritance:
trait GetInput {
fn get_input(&self);
}
impl GetInput for Player {
fn get_input(&self) {}
}
impl GetInput for NetPlayer {
fn get_input(&self) {}
}
// Fast: interface selected at compile time
fn do_something<T: GetInput>(player: T) {
player.get_input();
}
// Flexible: interface selected at run time (virtual call)
// Use with Box<Player> (see "Trait Objects")
fn do_something_dyn(player: &dyn GetInput) {
player.get_input();
}
However, Rust doesn't have inheritance of data. To have common fields shared between types, you need some alternative DIY solution (e.g. getters/setters in traits or structs with enums).