How to get the type name from `TypeId`?

I'm wondering if we can get the type_name from a TypeId instance? This will be useful when we're expecting one type but the user passes another, and we want to emit a friendly error. The Debug information of TypeId is not meaningful to human readers.

type_name_of_val doesn't help here if the value is a Box<dyn Any>.

I don't think this is possible. A type ID is just a hash computed from the various unspecified parts (field/variant names and types, layout, etc.) of a type. It inherently loses information. It only exists for comparing types for equality, but it is not reversible.

You might try to enforce that type IDs are always accessed through a caching API that stores the type name along with the ID and then later map the ID back to the type, like this:

#[derive(Default, Clone, Debug)]
struct TypeNameCache {
    map: HashMap<TypeId, &'static str>,
}

impl TypeNameCache {
    fn type_id_of<T: 'static>(&mut self) -> TypeId {
        let id = TypeId::of::<T>();
        let name = type_name::<T>();
        self.map.insert(id, name);
        id
    }
    
    fn name_for_id(&self, id: TypeId) -> Option<&'static str> {
        self.map.get(&id).copied()
    }
}
2 Likes

unfortunately Rust lacks any proper feature for meta programming
TypeID is just u64 generated by compiler uniquely for each type, theoretically compiler should keep association between type and this ID, so potentially it could be implemented, but I doubt it would be any time soon

This is not true. Rust has a great deal of compile-time metaprogramming capabilities, in the form of generics, macros, and consts. Perhaps you meant to write run-time reflection.

8 Likes

Something simple as class name is not necessary for run-time reflection.
Comparing to C++ templates, Rust's generics are pretty weak as you cannot really manipulate types all that well

1 Like

That's true, while proc macro is more powerful than C++ TMP.

I also believe it's possible to modify compiler to provide name from type id. Do you think this is a feature worth being discussed in internals?

In my view it is harmful because it will greatly increase the size of the binary generated. Imagine you storaged names of all types, including generics, trait object, all of them have a string. How scary the size it could be, let alone they are lessly used.
I think std::any::type_name is just something like macros, the string of name was provided in compile time.

1 Like

If we're talking about compiler intrinsics, that's probably how the proposed TypeId::name will be implemented.

You're right. That's unacceptable.

Syntax-wise, but semantic-wise this is opposite.

1 Like

Well, the opposite is true. It is possible to express all sorts of constraints and perform sophisticated computations using Rust's generics, doing so reliably. This is not the case in C++.

Before you try to argue that Rust's generics expressive please try to implement something complicated actually.
Trivial thing such as this is not available in Rust:

fn test<T: Copy>(input: &T) -> [u8; core::mem::size_of::<T>()] {
    todo!()
}

I need feature-full generics
Reliability is worthless if your generics lack minimal features to enable sophisticated generic code

1 Like

There is nothing trivial about const generics, or indeed any type system that mixes values and types. In fact support for it requires a pretty high degree of sophistication on the part of typeck.

Enabling an output type like in your example would require quite a lot of work. Perhaps it'll work when full const generics are stable, and that should tell you something about how difficult this is to achieve.

That may be true, but so is the reverse: (minimal) features are completely useless if they're not reliable when used.

I suspect you're looking forward to stabilization of full const generics. But this kind of chatter won't make it happen any faster I'm afraid.

8 Likes

Well, it's pretty rude to assume that I've never tried to implement anything non-trivial. I'm actually working on a database abstraction layer that embeds the AST of a typed query DSL into Rust purely using generics. It works just fine.

Then go ahead and use another language that has exactly what you need. Don't roam around Rust forums trolling the language and its users.

@DoumanAsh Not everything in Rust is where we would like it to be, but there is a lot of energy going into various problems. While we encourage bringing up cases that still need work, please be respectful while doing so.

@H2CO3 Please try to use more tact when addressing other people's behaviours. It's understandable if you can't afford that energy, but if that's the case please leave it to moderators to address.

4 Likes

I did report the comment in question to mods, but it's simply not possible to leave such a personal attack unaddressed with dignity.

2 Likes

I didn't ask you to not address it, I asked you to exercise more tact when doing so in the future. Just because someone else became less civil doesn't mean you should follow suit.

If you'd like to discuss this further, we can open a private thread.

4 Likes

Let me point out that typeid is unsound, because there is the possibiliy that two types have the same typeid. So there cannot be a way back. Also if you use different Compiler versions, you can get different ids. So, depending on the use case, it is better to have a hand-rolled trait.

Really? Because the first line in the doc says

A TypeId represents a globally unique identifier for a type.

Can you post a link about the unsound problem?

My type ID in question is retrieved from Any::type_id instead of TypeId::of, so this method won't work. Nevertheless it's a great idea. Thank you for posting that!