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?
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 ComponentAnd then downcast it whenever I need to use its data - Have a method like
execute_on_componentThat 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?