Downcasting Box<Any> to T, or working around


#1

Hi!

As part of a crazy loosely typed system I have a function that returns a Box<Any +'static> that I can cast to a type I know it is. Unfortunately when I try and do that cast I get this exception:

src/components/mod.rs:35:69: 35:83 error: the trait `core::marker::Reflect` is not implemented for the type `T` [E0277]
src/components/mod.rs:35 	let component_list : &'static SharedMutex<T> = *component_list_any.downcast_ref().expect("Invalid type for components list");
                         	                                                                   ^~~~~~~~~~~~~~

Seems like an easy fix, add Reflect to the type constraint of the Any, but that yields this exception:

src/components/mod.rs:12:5: 12:25 error: use of unstable library feature 'reflect_marker': requires RFC and more experience (see issue #27749)
src/components/mod.rs:12 use std::marker::Reflect;
                             ^~~~~~~~~~~~~~~~~~~~

That issue #27749 looks like it won’t soon be solved, some new system that isn’t done yet will first be implemented after which it is a bit unclear for me.

So that leaves me with the question: is there a workaround for this?

Alternatively, maybe I could redesign my system a bit, it’s totally in an exploratory fase, so I’m very much open to suggestions. I have included the full source of the file my problem is in, the problem function is called get_components_lock.

/// Components are types that associate an entity with a set of properties
/// for each type a list of all its instances is kept so they can be iterated
/// over efficiently by systems.
///
/// Whenever a new component is created it is added to a list.
///
/// component! { physics, body: physics.RigidBody, physics_id: physics.ID }
///

use std::collections::{ HashSet, HashMap };
use std::any::{ Any, TypeId };
use shared_mutex::{ SharedMutex, SharedMutexReadGuard, SharedMutexWriteGuard };

pub mod example;

#[derive(Clone)]
pub struct Component {
	pub name: TypeId,
	pub get_component_list: fn () -> Box<Any + 'static>
}

lazy_static! {
	pub static ref COMPONENTS: SharedMutex<HashMap<TypeId, Component>> = SharedMutex::new(HashMap::new());
}

pub fn register(component : Component) {
	let mut components = COMPONENTS.write().expect("COMPONENTS lock corrupted");
	components.insert(component.name, component);
}

pub fn get_components_lock<'mutex, T>(id : TypeId) -> SharedMutexReadGuard<'mutex, T> where T : 'static {
	let components = COMPONENTS.read().expect("COMPONENTS lock corrupted");
	let component = components.get(&id).expect("Unknown component type requested");
	let component_list_any = (component.get_component_list)();
	let component_list : &'static SharedMutex<T> = *component_list_any.downcast_ref().expect("Invalid type for components list");
	return component_list.read().expect("Component LIST lock corrupted"); 
}

#2

What’s the motivation for the design? So you have a known type Component that’s basically just a wrapper for a heap allocated thing that you want to share around?

Instead of externalising the cast from the Box<Any> to T could it be possible to introduce some stronger typing by making Component a trait so you have a RigidBodyPhysics component and a SoftBodyPhysics component for instance, instead of a single definition of Component that needs to do somersaults to abstract types?

Then you wouldn’t need get_component_list because you can downcast from Component to T. Unless there’s something fundamental I’m missing :smile:


#3

Adding T : Any+'static seems to work for your example.


#4

Ahh of course. That totally works, I don’t know why I was thrown off by the error message so much, I constrain to Any in various other places. It’s what I get for late night coding…

Thanks :slight_smile:


#5

Hi KodrAus,

I’m definitely not ruling out there’s a better design possible, but at the moment the components lists are statically defined and I don’t know where, so I need some kind of accessor or generic reference. I went with accessor here, I forgot why.

Thanks!