How does this code work?

pub trait MyFoo {
    fn as_any(&self) -> &dyn std::any::Any;

impl MyFoo for f32 {
    fn as_any(&self) -> &dyn Any {

impl MyFoo for u32 {
    fn as_any(&self) -> &dyn Any {

fn it_works() {
    let a = 1_u32;
    let b = 1_f32;

    let a_ptr: &dyn MyFoo = &a;
    let b_ptr: &dyn MyFoo = &b;

    println!("{:?}", a_ptr.as_any().downcast_ref::<u32>().is_some());
    println!("{:?}", a_ptr.as_any().downcast_ref::<f32>().is_some());

    println!("{:?}", b_ptr.as_any().downcast_ref::<u32>().is_some());
    println!("{:?}", b_ptr.as_any().downcast_ref::<f32>().is_some());}

this outputs t, f, f, t, as expected.

Question: how does this work. There seems to be no 'space' in the f32/u32 to store a tag. Is the type stored in the &dyn MyFoo itself ?

EDIT: To the best of my current mental model, for the downcast to behave correctly, as it does above, we need to store some 'tag' somewhere. I don't see how there is space to store this tag in the u32 / f32 itself. Therefore, where is it stored? Is it stored in the &dyn MyFoo ?

Yes, this is one of the reason why a dyn Trait is unsized and so &dyn Trait is a fat pointer. One of the sub-pointers points to the "data" (the value of the original underlying type) itself, while the other one points to the vtable, which holds type information.


The TypeId isn't in the vtable as far as I know. But the Any trait has the type_id method, so the base type's TypeId is still available via method call.



Okay, let's see if we can build a less wrong mental model for this.

The dyn trait itself is going to look something like:

struct fake_dyn_Trait {
  data: void*;
  vtable: vtable*;


x: &dyn MyFoo

the downcast<u32> ends up being something like:

if (x.vtable.as_any().type_id() == Type_id::of::<u32>()) {
  return Some( as *u32);
} else {
  return None;

Is this an approximate/plausible gist of what is going on ?

Indeed, there is no type information in the vtable itself, but it still at least uniquely identifies the type (my wording was intentionally vague because I wasn't sure exactly what usage of the vtable allows one to get to a concrete type). So based on that Any impl, it looks like that the type information is encoded in the body of the Any::type_id() method itself.

It's almost exactly like that – you can check the actual source code.


Yes, pretty much. Here's downcast_ref:

    pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
        if<T>() {
            // SAFETY: just checked whether we are pointing to the correct type, and we can rely on
            // that check for memory safety because we have implemented Any for all types; no other
            // impls can exist as they would conflict with our impl.
            unsafe { Some(&*(self as *const dyn Any as *const T)) }
        } else {

And here's is:

    pub fn is<T: Any>(&self) -> bool {
        // Get `TypeId` of the type this function is instantiated with.
        let t = TypeId::of::<T>();

        // Get `TypeId` of the type in the trait object (`self`).
        let concrete = self.type_id();

        // Compare both `TypeId`s on equality.
        t == concrete
1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.