How can I print the type of a variable?

fn main() {
    let x = 3;
    let y = 2.3;
    //how to print types of x, y.
    //i32, f32..
    println!("");
}

You don't.

The closest would be a type's TypeId, but even then you need to already know what the type is, and it looks like this:

fn main() {
    println!("{:?}", std::any::TypeId::of::<i32>());
}
TypeId { t: 10645063183773766558 }

If what you're actually trying to do is fine out what the type of an expression is, you can always ask the compiler by introducing a deliberate type mismatch:

fn main() {
    let _: () = 42;
}
<anon>:2:17: 2:19 error: mismatched types:
 expected `()`,
    found `_`
(expected (),
    found integral variable) [E0308]
<anon>:2     let _: () = 42;
                         ^~
<anon>:2:17: 2:19 help: see the detailed explanation for E0308
4 Likes

You can use TypeId without knowing the type:

fn typeid<T: std::any::Any>(_: &T) {
    println!("{:?}", std::any::TypeId::of::<T>());
}

struct Foo;
struct Bar;

fn main() {
    let f = Foo;
    typeid(&f);
    let b = Bar;
    typeid(&b);
    
    let x = 5;
    typeid(&x);
    println!("{:?}", std::any::TypeId::of::<i32>());
    
    let y = 5.;
    typeid(&y);
    println!("{:?}", std::any::TypeId::of::<f64>());
}
4 Likes

Thanks for help!

There are perfectly legitimate reasons to want to print types. One I'm working with right now is a case where I had to erase the type on a pointer and place the TypeId next to it so I can perform a checked dynamic cast later. If I've made an error in performing this checked cast, I'd like to be able to indicate the before and after type to help me debug this information after the fact.

Yeah, a slightly more helpful way to answer the question might be...

Printing the name of a type at runtime is complicated in Rust, largely because types don't really exist at runtime. In a language like Java, objects contain class pointers that indicate their type, and you can often reach the type's name through them (o.getClass().getName() if I remember my Java correctly). Rust doesn't include type information in every value, so that approach is right out.

There are some alternatives, but none is perfect.

If you're in a context where you "know" the type -- say, a generic function -- there is an unstable intrinsic type_name::<T>(). The "unstable" part means you're stuck on the nightly compiler, which may or may not work for you. (Edit: see @leudz's post below, it has been re-exported as a stable API when I wasn't looking!)

In your case, it sounds like you're doing something much like Any, where you store a typeid and check equality later to implement a protected downcast. One of the ergonomic issues with Any is that, if the downcast fails, it can't explain why or print the type name, for the same reason you're having trouble. There doesn't appear to be a way to go from a typeid to a string name right now. At the cost of an additional pointer, you could capture the name (perhaps using that unstable intrinsic) when the concrete type is being erased, and so you wind up storing (typeid, &str, T).

There's also the typename crate, which is an alternative to the intrinsic. It derives a trait for types whose names can be obtained. However, you have to impl that trait for any type you want to talk about, which seems limiting. Plus, in a type-erased context, you need to be holding a &dyn TypeName to get the right vtable... not a &dyn Any that would let you downcast. (Though I suppose you could ask TypeName when you erase the type and store the result, just like I suggested above.) Also, TypeName allocates (it returns a String rather than a &'static str), and while I understand why, allocations still make me sad, and I bet you don't want to incur a string allocation every time a type gets erased. (But I could be wrong!)

Hope that helps.

2 Likes

The intrinsic is unstable but there's a stable "re-export" since 1.38: any::type_name.

3 Likes