Search code examples
typesrustintrospection

Possible to access the 'TypeId' of a struct member?


Is there a way to access the TypeId (std::any::TypeId::of::<T>) of a struct member by name?

If I have a basic struct:

MyStruct {
    value: i64,
}

And I only know MyStruct and value, is there a way to access TypeId::of::<i64> - where i64 depends on the type of value?

main () {
    assert_eq!(
        TypeId::of::<i64>,
        // ^^^ this works
        type_id_of!(MyStruct, value),
        // ^^^ this is what I'm looking for
    );
}

See related question: Is it possible to access the type of a struct member for function signatures or declarations?


Solution

  • You can use type detection to deduce the TypeId of any field of a value you have, as long as it's 'static (other TypeId::of doesn't work):

    fn type_id<T: 'static + ?Sized>(_: &T) -> TypeId {
        TypeId::of::<T>()
    }
    
    fn main() {
        let m = MyStruct { value: 4 };
        println!("{:?} {:?}", TypeId::of::<i64>(), type_id(&m.value));
    }
    

    Then, leveraging the strategy in the offsetof question you asked, you can make a macro to get it from a type without having an instance:

    macro_rules! type_id_of {
        ($t:ty, $f:ident) => {
            {
                fn type_of<T: 'static + ?Sized>(_: &T) -> TypeId {
                    TypeId::of::<T>()
                }
                let base: $t = unsafe { ::std::mem::uninitialized() };
                let result = type_of(&base.$f);
                ::std::mem::forget(base);
                result
            }
        }
    }
    
    fn main() {
        println!("{:?} {:?}", TypeId::of::<i64>(), type_id_of!(MyStruct, value));
    }