I have been trying to do this for 3 days straight and I just can't do it, is this even possible in safe rust?

76 points l3wl3w00

Okay, so first, for context here is the necessary code:

This is the very beggining of a 2d game using vulkan, my main goal is just to get more familiar with both rust and vulkan

I have a game object that contains its components in a hashmap.

pub struct GameObject {
    id: GameObjectId,
    components: HashMap<TypeId, Rc<RefCell<dyn Component>>>
}

I have a Component trait, that defines the tick method signature so far

pub trait Component {
    fn as_any(&self) -> &dyn Any;
    fn as_any_rc(self: Rc<Self>) -> Rc<dyn Any>;
    fn tick(&mut self, input_provider: &InputProvider, game_object: &GameObject);
}

And I have a component data trait just to be able to explicity signal that a struct is meant to be a component

pub trait ComponentData {}

And just as an example, this is what a struct that implements it looks like

#[derive(Copy, Clone)]
pub struct Transform {
    position: Vector3<f32>,
    rotation: Rad<f32>,
    scale: Vector2<f32>,
}

impl ComponentData for Transform {}

And then I have a wrapper class that also has a weak reference to a game object. This is the struct that contains the behaviour and implements Component

pub struct ComponentOf<T: ComponentData> {
    game_object: Weak<RefCell<GameObject>>,
    data: T,
}
impl Component for ComponentOf<Transform> {
    fn as_any(&self) -> &dyn Any {
        self
    }
    fn as_any_rc(self: Rc<Self>) -> Rc<dyn Any> {
        self
    }
    fn tick(&mut self, input_provider: &InputProvider, game_object: &GameObject) {

    }
}

So, the problem is, I want a method on the game object that returns an optional borrowed reference to a component downcasted to a provided type. I have tried it in like 3 million ways, I could never get the code to compile. Is this even possible so that the function is not unsafe (I dont mind unsafe blocks inside, I just want the method to be safe, I think this is a fair expectation)? I do not wanna clone the data. I have tried to return

  • Option<Rc<RefCell<ComponentOf<T>>>>
  • Option<Rc<ComponentOf<T>>>
  • Option<Weak<ComponentOf<T>>>
  • Option<Weak<RefCell<ComponentOf<T>>>>
  • Option<Rc<&ComponentOf<T>>>
  • Option<&ComponentOf<T>>

I have also tried to not use RefCell at all, but I think this is needed here to be able to mutate the game object. I dont understand refcell very thouroughly, but I get the idea, still I might be wrong here.

I would not like to go back to each possible solution I have had, but I have literally tried everything I could think of, here is the current state of the code:

pub fn get_component<T: ComponentData + 'static>(&self) -> Option<&ComponentOf<T>> {
    let component = self.components.get(&TypeId::of::<ComponentOf<T>>())?;
    let as_any = component.borrow().as_any();
    as_any.downcast_ref::<ComponentOf<T>>()
} 

The error I get is this:

(Edit here: the previous error message was wrongly copy pasted and not the actual error message I was getting)

error[E0515]: cannot return value referencing temporary value
  --> src\\game\\game_object.rs:51:9
   |
50 |         let as_any = component.borrow().as_any();
   |                      ------------------ temporary value created here
51 |         as_any.downcast_ref::<ComponentOf<T>>()
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ returns a value referencing data owned by the current function

I think I get why this is exact error is happening, but I just cant fix this.

I have tried to think of alternatives, like

  • Just return a &dyn Component And then downcast it whenever I need to use its data
  • Have a method like execute_on_component That queries the component and takes a closure that does some operation on the component if found
  • Use raw pointers, but then I can only use this method in unsafe context, which is not ideal

but ultimately it just annoys me very much that I can't do exactly what I want.

Can somebody please confirm that this is not possible or show me a way to do this?