Still in my learning of rust while trying to make an ECS, I ecountered this issue :
I'm having a hashmap as a component table, where the keys are components ids and values are components arrays. Components array is a wrapper around another hashmap, where keys are entity ids and values are the components (so components arrays are generics with the type of component).
Here is the structure :
pub struct ComponentTable {
components: HashMap<u32, Box<dyn ComponentArrayOnly>>,
}
pub struct ComponentArray<C> {
comp_array: HashMap<u64, C>,
}
impl<C> ComponentArray<C> {
pub fn new() -> ComponentArray<C> {
return ComponentArray {
comp_array: HashMap::new(),
};
}
}
trait ComponentArrayOnly {
fn add_component(self, entity_id: u64);
}
impl<C: Component> ComponentArrayOnly for ComponentArray<C> {
fn add_component(mut self, entity_id: u64) {
if !self.comp_array.contains_key(&entity_id) {
self.comp_array.insert(entity_id, C::new());
}
}
}
(the trait component array only is to hide the generics in the component table)
Now, when I try to add components from the component table, which should check if the component array exists (if not, add it) then call the add_component method on the array, I'm getting an error :
impl ComponentTable {
pub fn new() -> ComponentTable {
return ComponentTable {
components: HashMap::new(),
};
}
pub fn add_component_to<C: Component + 'static>(mut self, entity: u64) {
if !self.components.contains_key(&C::id()) {
// add a new component array
self.components.insert(C::id(), Box::new(ComponentArray::<C>::new()));
}
// add the component at the entity id in the component array
match self.components.get(&C::id()) {
None => println!("Unable to add component : component array was not created !"),
Some(c_arr) => c_arr.add_component(entity),
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// error[E0507]: cannot move out of `**c_arr` which is behind a shared reference
// move occurs because `**c_arr` has type `dyn ComponentArrayOnly`, which does not implement the `Copy` trait
}
}
}
I don't really understand what is going on. I don't want c_arr to be a shared reference, I would like to borrow it to modify it with add_component then release it back in the hashmap.
I looked the HashMap documentation but couldn't find anything to achieve this.
Am I missing something? or is just poor design choices?
As mentionned in the comments, the issue was that I was getting the ComponentArray by value and not reference when adding components to it.
So I needed to slightly change the implementation of add_component on the component array :
trait ComponentArrayOnly {
fn add_component(&mut self, entity_id: u64);
}
impl<C: Component> ComponentArrayOnly for ComponentArray<C> {
fn add_component(&mut self, entity_id: u64) {
if !self.comp_array.contains_key(&entity_id) {
self.comp_array.insert(entity_id, C::new());
}
}
}
And borrow the component array as a mut from the hashmap :
match self.components.get_mut(&C::id()) {
None => println!("Unable to add component : component array was not created !"),
Some(c_arr) => c_arr.add_component(entity),
}
Related
Here's an example of a problem I ran into:
pub struct Item {
name: String,
value: LockableValue, // another struct that I'd like to mutate
}
impl Item {
pub fn name(&self) -> &str {
&self.name
}
pub fn value_mut(&mut self) -> &mut LockableValue {
&self.value
}
}
pub fn update(item: &mut Item) {
let value = item.value_mut();
value.change(); // how it changes is unimportant
println!("Updated item: {}", item.name());
}
Now, I know why this fails. I have a mutable reference to item through the mutable reference to the value.
If I convert the reference to an owned String, it works fine, but looks strange to me:
pub fn update(item: &mut Item) {
let name = { item.name().to_owned() };
let value = item.value_mut();
value.change(); // how it changes is unimportant
println!("Updated item: {}", name); // It works!
}
If I let value reference drop, then everything is fine.
pub fn update(item: &mut Item) {
{
let value = item.value_mut();
value.change(); // how it changes is unimportant
}
println!("Updated item: {}", item.name()); // It works!
}
The value.change() block is rather large, and accessing other fields in item might be helpful. So while I do have solutions to this issue, I'm wondering if there is a better (code-smell) way to do this. Any suggestions?
My intention behind the above structs was to allow Items to change values, but the name should be immutable. LockableValue is an tool to interface with another memory system, and copying/cloning the struct is not a good idea, as the memory is managed there. (I implement Drop on LockableValue to clean up.)
I was hoping it would be straight-forward to protect members of the struct from modification (even if it were immutable) like this... and I can, but it ends up looking weird to me. Maybe I just need to get used to it?
You could use interior mutability on only the part that you want to mutate by using a RefCell like ths:
use std::cell::{RefCell, RefMut};
pub struct LockableValue;
impl LockableValue {
fn change(&mut self) {}
}
pub struct Item {
name: String,
value: RefCell<LockableValue>, // another struct that I'd like to mutate
}
impl Item {
pub fn name(&self) -> &str {
&self.name
}
pub fn value_mut(&self) -> RefMut<'_, LockableValue> {
self.value.borrow_mut()
}
}
pub fn update(item: &Item) {
let name = item.name();
let mut value = item.value_mut();
value.change(); // how it changes is unimportant
println!("Updated item: {}", name);
}
That way you only need a shared reference to Item and you don't run into an issue with the borrow checker.
Not that this forces the borrow checks on value to be done at runtime though and thus comes with a performance hit.
I am trying to define a trait for algorithm that returns a struct that reference multiple internal fields after each step in the algorithm. Then the calling code can optionally choose to serialize the state information or maybe display it. I want to return this as references so that if the calling code doesn't use it, there is not hit to performance (the internal state could be very large).
Below is what I think I want, but instead I want it to be generic of an Algorithm trait and then implementing the Algorithm trait for FooAlgorithm, or others.
use serde::{Deserialize, Serialize}; // 1.0.125
struct FooStateRef<'a> {
field_0: &'a usize,
field_1: &'a usize,
}
impl<'a> Clone for FooStateRef<'a> {
fn clone(&self) -> Self {
FooStateRef {
field_0: self.field_0,
field_1: self.field_1,
}
}
}
impl<'a> Copy for FooStateRef<'a> {}
#[derive(Debug, Serialize, Deserialize)]
struct FooState {
field_0: usize,
field_1: usize,
}
struct FooAlgorithm {
field_0: usize, // Don't want to clone unless the calling code wants to serialize
field_1: usize, // Don't want to clone unless the calling code wants to serialize
field_2: usize, // No need to serialize
}
impl<'a> From<FooStateRef<'a>> for FooState {
fn from(state: FooStateRef) -> FooState {
FooState {
field_0: state.field_0.clone(),
field_1: state.field_1.clone(),
}
}
}
impl FooAlgorithm {
fn step(&mut self) -> Option<FooStateRef> {
self.field_1 += self.field_0;
self.field_2 += self.field_1;
self.field_0 += self.field_2;
if self.field_2 > 100 {
Some(FooStateRef {
field_0: &self.field_0,
field_1: &self.field_1,
})
} else {
None
}
}
}
fn main() {
let mut algo = FooAlgorithm {
field_0: 1,
field_1: 1,
field_2: 1,
};
let mut count = 0;
loop {
match algo.step() {
Some(state_ref) => {
if count % 10 == 0 {
// Save state to file
}
}
None => break,
}
count += 1;
}
}
My attempt at making the Algorithm trait is here: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=5a36c6eb121f0724284bf7d5ec5a8cad
The issue I'm running into is with lifetimes. How do I write the function signature in the algorithm trait definition so that I can return a FooStateRef<'a> when I implement Algorithm for FooAlgorithm, but also let me return BarStateRef<'a> when I implement Algorithm for BarAlgorithm?
Another thing I have looked into is associated types, but from what I can tell, I would need Generic Associated Traits to add something like type AlgoStateRef<'a>: StateRef<'a>; to the Algorithm trait definition.
I have code like this:
pub trait Action {
fn execute(&self);
}
struct AddAction<'a> {
rpn_calculator: &'a RpnCalculator
}
struct DeductAction<'a> {
rpn_calculator: &'a RpnCalculator
}
impl Action for DeductAction<'_> {
fn execute(&self) {
// ...
}
}
impl Action for AddAction<'_> {
fn execute(&self) {
// ...
}
}
impl<'a> RpnCalculator {
fn actions(&self) -> Vec<Box<dyn Action + 'a>> {
let mut actions: Vec<Box<dyn Action + 'a>> = vec![
Box::new(AddAction { rpn_calculator: &self })
Box::new(AddAction { rpn_calculator: &self })
// ...
];
// ...
actions
}
}
The intention of my code is that RpnCalculator.actions() should create some instances of some structs that implement trait Action and return a vector containing those instances. Those structs have a property rpn_calculator which is a reference to a RpnCalculator. The RpnCalculator.actions() should put self (the RpnCalculator that creates it) into this reference.
Now the error I get is "cannot infer the appropiate lifetime". I get this error in the line where I create an instance that I add to the vector:
Box::new(AddAction { rpn_calculator: &self })
For that reason I have 'a in the vector declaration, but it still doesn't work.
You should probably use fn actions(&'a self) because the lifetime
'a you use in dyn Action + 'a is related to the lifetime
of the RpnCalculator.
I am looking for your feedback/advice on a piece of code.
Basically, I have a SOA like this one:
struct Entities {
pub meshes: FakeArena<Mesh>,
pub lights: FakeArena<Light>,
}
I can access a particular value through his “handle” (every handle is bound to specific type), so I could get the value of a mesh by doing entities.meshes.get(&handle).
So far, so good, but I’m trying to achieve this by dynamically retrieving the value through the corresponding arena. By doing entities.get(&handle) if the handle type is Mesh, I return entities.meshes.get(&handle). My Entities struct has a method called get:
fn get<T: Any>(&self, handle: &Handle<T>) -> &T {
let mut entity: Option<&dyn Any> = None;
let any = handle as &dyn Any;
any.downcast_ref::<Handle<Mesh>>()
.map(|handle| entity = Some(self.meshes.get(handle) as &dyn Any));
any.downcast_ref::<Handle<Light>>()
.map(|handle| entity = Some(self.lights.get(handle) as &dyn Any));
if entity.is_none() {
panic!("Type not found in stored entites.");
}
entity
.unwrap()
.downcast_ref::<T>()
.expect("Error while downcasting the entity type")
}
Playground
This works perfectly. I downcast the generic type into a concrete one, then back again into a generic one, but it seems weird and tricky.
Maybe I'm missing something, or maybe you have a better idea for this; what would you do? :)
You don't require any dynamic dispatch here, plain-old static dispatch should be enough.
Create a trait which is given a reference to your container struct. Each component type implements this trait and selects the appropriate field of the container. Then, require the trait in your get method and use it:
struct Mesh;
struct Light;
struct Entities {
meshes: Vec<Mesh>,
lights: Vec<Light>,
}
trait Example {
fn get_in<'a>(&self, entities: &'a Entities) -> &'a Self;
}
impl Example for Mesh {
fn get_in<'a>(&self, entities: &'a Entities) -> &'a Self {
&entities.meshes[0]
}
}
impl Example for Light {
fn get_in<'a>(&self, entities: &'a Entities) -> &'a Self {
&entities.lights[0]
}
}
impl Entities {
fn get<T: Example>(&self, handle: T) -> &T {
handle.get_in(self)
}
}
fn example(entities: &Entities) {
let m = entities.get(Mesh);
let l = entities.get(Light);
}
Basically a object (struct) is constructed by composing different components. Each concrete component being easily swapped by another component matching the interface (I guess trait).
I'm currently trying to implement with traits which got me into some errors and made me start thinking if this is a common thing in Rust.
// usage example
fn main() {
let obj = MainObject::new(Component1::new(), Component2::new(), Component3());
// Where each component is a type(struct) with some well predefined methods.
}
The main idea behind this is to implement the Component pattern commonly used in games. Basically the game would contain a lot of different objects, with slight variations in behavior and contained data. Instead of having a big class hierarchy, the objects are composed of standard components, more complete example would be.
pub struct Container
{
input: InputHandlerComponent, // Probably a trait
physics: PhysicsComponent, // Probably a trait
renderer: RendererCompoent // Probably a trait
}
impl Container {
fn new(p: PhysicsComponent, i: InputComponent, r: RenderComponent) -> Container {
Container {input: i, physics: p, renderer: r}
}
}
struct ConcretePhysicsComponent;
impl PhysicsComponent for ConcretePhysicsComponent
{
// ...
}
struct ConcreteInputComponent;
impl InputComponent for ConcreteInputComponent
{
// ...
}
struct ConcreteRendererComponent;
impl RendererComponent for ConcreteRendererComponent
{
// ...
}
struct AnotherConcreteRendererComponent;
impl RendererComponent for AnotherConcreteRendererComponent
{
// ...
}
// usage example
fn main() {
let obj = Container::new(ConcreteInputComponent::new(), ConcretePhysicsComponent::new(), ConcreteRendererComponent::new());
// Where each component is a type(struct) with some well predefined methods.
// This is a slightly modified version of this object, with changed rendering behaviour
let obj2 = Container::new(ConcreteInputComponent::new(), ConcretePhysicsComponent::new(), AnotherConcreteRendererComponent::new()); }
It sounds like you are just asking about traits, multiple concrete implementations of that trait, and a wrapper object that restricts itself to types that implement that trait. Optionally, the container can implement the trait by delegating it to the inner object.
trait Health {
fn life(&self) -> u8;
fn hit_for(&mut self, lost_life: u8);
}
#[derive(Debug, Copy, Clone)]
struct WimpyHealth(u8);
impl Health for WimpyHealth {
fn life(&self) -> u8 { self.0 }
fn hit_for(&mut self, lost_life: u8) { self.0 -= lost_life * 2; }
}
#[derive(Debug, Copy, Clone)]
struct BuffHealth(u8);
impl Health for BuffHealth {
fn life(&self) -> u8 { self.0 }
fn hit_for(&mut self, lost_life: u8) { self.0 -= lost_life / 2; }
}
#[derive(Debug, Copy, Clone)]
struct Player<H> {
health: H,
}
impl<H> Health for Player<H>
where H: Health
{
fn life(&self) -> u8 { self.health.life() }
fn hit_for(&mut self, lost_life: u8) { self.health.hit_for(lost_life) }
}
fn main() {
let mut player_one = Player { health: WimpyHealth(128) };
let mut player_two = Player { health: BuffHealth(128) };
player_one.hit_for(12);
player_two.hit_for(12);
println!("{:?}", player_one);
println!("{:?}", player_two);
}
it is not possible to have an array of such Players without using Boxed values
That's correct. An array or vector (or any generic type, really) needs to all be of the same type. This is especially important for arrays/vectors because their memory layout is contiguous and each item needs to be at a fixed interval.
If you were allowed to have different types, then you could have one player that had a health that took 1 byte and another player with health that took 2 bytes. Then all the offsets would be incorrect.
You can implement the Health trait for a Box<Health>, and then the Player objects can be stored sequentially, but they would each have a pointer to the appropriate concrete implementation of Health via the box.
impl<H: ?Sized> Health for Box<H>
where H: Health
{
fn life(&self) -> u8 { (**self).life() }
fn hit_for(&mut self, lost_life: u8) { (**self).hit_for(lost_life) }
}
fn main() {
let mut players = vec![
Player { health: Box::new(WimpyHealth(128)) as Box<Health> },
Player { health: Box::new(BuffHealth(128)) as Box<Health> }
];
for player in players.iter_mut() {
player.hit_for(42);
}
println!("{:?}", players[0].life());
println!("{:?}", players[1].life());
}