Rust: Intrinsic for `type_name_of_id` to power a better `impl Debug for TypeId`?

Created on 5 Jun 2019  路  7Comments  路  Source: rust-lang/rust

Currently TypeIds have uninformative derived Debug impls:

fn main() {
    println!("{:?}", std::any::TypeId::of::<usize>());
}
   Compiling playground v0.0.1 (/playground)
    Finished dev [unoptimized + debuginfo] target(s) in 0.91s
     Running `target/debug/playground`
TypeId { t: 8766594652559642870 }

This results in fairly poor Debug output for dynamic types like anymap.

I think it could be quite nice for debugging/logging/etc to allow printing the type name from a TypeId in the Debug impl. It would provide an out of the box improvement to debugging existing dynamic typing tools, and IIUC the contents of Debug impls in the standard library are not considered stable so there's neither a breaking change here nor a de facto stabilization of the type_name representation.

I assume this would need to rely on some unstable intrinsic being exposed to get the type_name of an ID at run time, but I'm not really aware what would be needed.

Thoughts? cc @oli-obk as we had discussed this a bit on IRC.

T-lang T-libs needs-fcp

Most helpful comment

I think this is a cool idea. However, to implement this we would probably have to add some sort of type table to each application binary. That seems like it could become extremely expensive as each instantiation of a generic type would be stored separately.

All 7 comments

I think this is a cool idea. However, to implement this we would probably have to add some sort of type table to each application binary. That seems like it could become extremely expensive as each instantiation of a generic type would be stored separately.

The amount of extra memory needed is fairly limited, as most of these type names will already have ended up somewhere in the binary. So we could just have the other places refer to the table entries instad of having their own version of the name.

For release builds we could even just skip this table entirely and fall back to id printing.

Alternatively we can make the TypeId struct have a &'static str field in debug mode and just fill it in :laughing:

Alternatively we can make the TypeId struct have a &'static str field in debug mode and just fill it in 馃槅

Ostensibly we need something like this to deal with the soundness hole in https://github.com/rust-lang/rust/issues/10389?

oh, you mean the solution where each TypeId is in fact backed by a (non-deduplicateable and non-duplicatable) static item and the TypeId is just a pointer to that? And the value of that static could be the type's name (or a zero length string in the release-mode version).

Interesting ideas! If I had my druthers it would be feasible to use this in release mode as well, as I am interested in using the output for logging.

@oli-obk Something in that spirit yeah.

I imagine this as follows.

We would have a:

trait TypeName {
    fn name(&self) -> &'static str;
}

that is implemented for all types:

impl<T: ?Sized> TypeName for T {
    #[cfg(rtti)]
    #[lang_item = "type_name_blanket_impl"]
    fn name(&self) -> &'static str;
    #[cfg(not(rtti))]
    fn name(&self) -> &'static str { "rtti-disabled" }
}

Then we would have a global compiler switch, e.g., -C rtti=on/off, that controls what the trait impls do. When -C rtti=on, TypeName::type_name implementations are magically generated to return the &'static str containing the name of the type are added for each type in the whole program. When -C rtti=on, only a blanket impl, e.g., returning "rtti-disabled", is provided.

Was this page helpful?
0 / 5 - 0 ratings