Downcasting Any using TypeId

I am writing a module for generic data processing. I have to store a list of generic structs and retrieve them later. I am using Any to store those structs in a vector. But I have no way of knowing the type to downcast them later. Therefore I tried using typeIds to downcast, but I am stuck now. Can anyone help me with this? Thanks

Here is an example script which describes what I am trying to do.


use std::marker::PhantomData;
use std::fmt:: Debug;
use std::any::{Any, TypeId};

struct generic_struct<T: Debug>{
    value: T,
}

fn insert<T: 'static + Debug>(s: generic_struct<T>, map: &mut Vec<(TypeId, Box<Any>)>) {
    map.push((TypeId::of::<generic_struct<T>>(), Box::new(s)))
}

fn main() {
    let mut map:Vec<(TypeId, Box<Any>)> = Vec::new();
    let f = generic_struct{value: 5};
    insert(f, &mut map);
    let f = generic_struct{value: "f"};
    insert(f, &mut map);
    let f = generic_struct{value: 5.0};
    insert(f, &mut map);
    let f = generic_struct{value: 5.0};
    insert(f, &mut map);
    for (typeid ,value) in map{
    println!("{:?} {:?}", typeid, value);
//        if let Some(s) = value.downcast_ref::<typeid>() { //Somehow downcast using typeid instead of type
//            println!("{:?}", s);
//        }
    }
}
1 Like

Hi!
Unfortunately what you're doing isn't possible. TypeId is just a number, referring to the type passed into std::any::TypeId::of::<T>(), and therefore not something that can be evaluated at runtime. Because it cannot be evaluated at runtime, it has no relation to the actual type at compile time. Therefore, you cannot use just a TypeId to downcast a dyn Any because there is no type to downcast to.
Therefore, you need to use the type to solve this. If you just needed a trait object T where T: !Sized + Debug as seen in your example, you could use T = (dyn Debug). In theory using some dynamic trait object pointer magic, you could cast back and forth between a (dyn Debug) and a (dyn Any), but it would be rather verbose, and difficult to use for multiple traits, and it would use alot of unsafe code, which is almost always a no-no with trait objects because their definition in memory isn't cemented.

1 Like

By the way, you can use the Any trait to get the TypeId of the thing it stores, using the type_id method on Any. So there isn't much of a point in storing it.

Now if you have no idea what type you have, do you at least know what is the set of types you are working with? If so you can create a function that converts the Box<dyn Any> into an enum of all of the other types. In fact I would recommend that you first try your hand at using enums instead of Any. Any is usually the wrong tool for the job.

2 Likes

unfortunately I can't know all the types. I have set of dependencies for each generic struct depending on the previous nodes in the processing graph. Each dependency is dependent on generic struct. In scala, if I have to store those dependencies in a list I can do like this, List[Dependency[ _ ]]

Ah, that's a problem. Do you have to be able to down cast them? Or could you build an interface to work through? Basically can you just work with trait objects instead of trying to downcast them? What exactly is your problem? If we know we can help you more.

1 Like

It's like this. User provides the data and closure to transform that data.
I store that data and closure in a struct implementing a particular trait. For subsequent transformations, I generate more such structs. When there is one to one dependency between transformation, everything is fine. In some transformations, there are shared dependencies which I am storing in a Vector as you mentioned. But when I actually have to perform the transformation, I have to downcast them to their concrete types.
In Scala, I can actually downcast the dependency as dependency.asInstanceOf[Dependency[(Any,Any)] and still can perform the transformation using the closure.
In Rust, I am not able to do that.

Only restriction on the data the user provides is that it should be cloneable and serializable.

So let me check, you are storing a bunch of different types in a Vec<Box<dyn Any>> and some closures that perform transformations on those types, and you need to be able to use the concrete types to pass to the closures so that the types checkout.

Yeah exactly.

Ok, so could you use something like this

trait Dependency {
   fn do_work(&mut self);
}

struct ConcreteDependency<T, F> {
    concrete_type: T,
    closure: F
}

impl<T, F> Dependency for ConcreteDependency<T, F>
where F: FnMut(&mut T) {
    fn do_work(&mut self) {
        (self.closure)(&mut self.concrete_type)
    }
}

// .. later ..

let mut x : Vec<Box<dyn Dependency>> = Vec::new();

// .. fill it in ..

x[2].do_work();
3 Likes

I think this will do. Thanks a lot.

1 Like

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.