449

I have the following:

let mut my_number = 32.90;

How do I print the type of my_number?

Using type and type_of did not work. Is there another way I can print the number's type?

t0r0X
  • 4,212
  • 1
  • 38
  • 34
user2431012
  • 4,607
  • 2
  • 13
  • 8

18 Answers18

404

You can use the std::any::type_name function. This doesn't need a nightly compiler or an external crate, and the results are quite correct:

fn print_type_of<T>(_: &T) {
    println!("{}", std::any::type_name::<T>())
}

fn main() {
    let s = "Hello";
    let i = 42;

    print_type_of(&s); // &str
    print_type_of(&i); // i32
    print_type_of(&main); // playground::main
    print_type_of(&print_type_of::<i32>); // playground::print_type_of<i32>
    print_type_of(&{ || "Hi!" }); // playground::main::{{closure}}
}

Be warned: as said in the documentation, this information must be used for a debug purpose only:

This is intended for diagnostic use. The exact contents and format of the string are not specified, other than being a best-effort description of the type.

If you want your type representation to stay the same between compiler versions, you should use a trait, like in the phicr's answer.

Boiethios
  • 38,438
  • 19
  • 134
  • 183
270

If you merely wish to find out the type of a variable and are willing to do it at compile time, you can cause an error and get the compiler to pick it up.

For example, set the variable to a type which doesn't work:

let mut my_number: () = 32.90;
// let () = x; would work too
error[E0308]: mismatched types
 --> src/main.rs:2:29
  |
2 |     let mut my_number: () = 32.90;
  |                             ^^^^^ expected (), found floating-point number
  |
  = note: expected type `()`
             found type `{float}`

Or call an invalid method:

let mut my_number = 32.90;
my_number.what_is_this();
error[E0599]: no method named `what_is_this` found for type `{float}` in the current scope
 --> src/main.rs:3:15
  |
3 |     my_number.what_is_this();
  |               ^^^^^^^^^^^^

Or access an invalid field:

let mut my_number = 32.90;
my_number.what_is_this
error[E0610]: `{float}` is a primitive type and therefore doesn't have fields
 --> src/main.rs:3:15
  |
3 |     my_number.what_is_this
  |               ^^^^^^^^^^^^

These reveal the type, which in this case is actually not fully resolved. It’s called “floating-point variable” in the first example, and “{float}” in all three examples; this is a partially resolved type which could end up f32 or f64, depending on how you use it. “{float}” is not a legal type name, it’s a placeholder meaning “I’m not completely sure what this is”, but it is a floating-point number. In the case of floating-point variables, if you don't constrain it, it will default to f64¹. (An unqualified integer literal will default to i32.)

See also:


¹ There may still be ways of baffling the compiler so that it can’t decide between f32 and f64; I’m not sure. It used to be as simple as 32.90.eq(&32.90), but that treats both as f64 now and chugs along happily, so I don’t know.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Chris Morgan
  • 86,207
  • 24
  • 208
  • 215
  • 4
    `:?` has for quite a long time now been manually implemented. But more importantly, the `std::fmt::Debug` implementation (for that is what `:?` uses) for number types no longer includes a suffix to indicate which type it is of. – Chris Morgan May 02 '15 at 12:40
  • 5
    I use these techniques a lot for trying to find the type of an expression, but it doesn't always work, especially when there are type parameters involved. The compiler will, for example, tell me that it's expecting an `ImageBuffer<_, Vec<_>>` which doesn't help me very much when I'm trying to write a function that takes one of these things as a parameter. And this happens in code that otherwise compiles until I add the `:()`. Is there no better way? – Christopher Armstrong Dec 12 '16 at 23:37
  • 2
    This seems to be a bit convoluted and unintuitive. Would it be very difficult for the code editor e.g. Emacs provide the type when the cursor rests on the variable, like in many other languages? If the compiler can tell the type upon error, surely it should also already know the type when there isn't any error? – xji Jan 16 '17 at 18:09
  • 1
    @JIXiang: the Rust Language Server is all about providing this information to an IDE, but it’s not mature yet—its first alpha release was only a couple of days ago. Yes, this is an eldritch approach; yes, less esoteric ways of achieving the goal are steadily coming. – Chris Morgan Jan 19 '17 at 04:19
  • lol, but it works - does anyone know if there are editors that would tell you on hover (like f# visualstudio but not VS)? I am using sublime but nothing like that i see.. – schmoopy Mar 04 '17 at 21:16
  • 5
    this sounds very much like a hack. is this actually the idiomatic way to check the type of a variable? – confused00 Nov 18 '18 at 14:12
  • @confused00: there is no idiomatic way of doing it, because it’s an operation that fundamentally doesn’t make sense in a language like Rust as a straight language or compiler feature—you can devise ways to do it half-heartedly, but it’s just not something you should ever want to do in real life. For extracting the information from the code, for an IDE or similar, you want the Rust Language Server. – Chris Morgan Nov 19 '18 at 03:46
  • why does it not make sense? how am I supposed to learn the type of value returned from a function in a library without docs? (aside from checking the function definition in the code) – confused00 Nov 19 '18 at 08:53
  • @confused00: types are a compile-time feature, not a run-time feature; given Rust’s goals and purposes, retrieving types at run-time just doesn’t make sense. There are decidedly unidiomatic solutions, as shown in the various answers here, but there is no idiomatic solution. I’ve mentioned RLS which is the sort of tooling that should be used for this operation. – Chris Morgan Nov 21 '18 at 01:53
  • 1
    @ChrisMorgan a current example: I had to hack the code to see that one part generated HashMap and the other HashMap<&String,&i32> (and resulted all kinds of errors neither TheBook nor Google had a fix for), and I have currently no _sane_ way to help me see that. That's very bad for rust beginners. – grin Jan 13 '19 at 00:49
  • The `variable.does_not_exist` approach is better because it gives you borrow/reference information along with the type - the `let () = variable` approach just gives you the struct name. – joe Aug 23 '19 at 11:59
142

There is an unstable function std::intrinsics::type_name that can get you the name of a type, though you have to use a nightly build of Rust (this is unlikely to ever work in stable Rust). Here’s an example:

#![feature(core_intrinsics)]

fn print_type_of<T>(_: &T) {
    println!("{}", unsafe { std::intrinsics::type_name::<T>() });
}

fn main() {
    print_type_of(&32.90);          // prints "f64"
    print_type_of(&vec![1, 2, 4]);  // prints "std::vec::Vec<i32>"
    print_type_of(&"foo");          // prints "&str"
}
blackgreen
  • 34,072
  • 23
  • 111
  • 129
Shubham Jain
  • 1,429
  • 1
  • 9
  • 5
  • 2
    On rust-nightly (1.3) it only worked when changing that first line to `#![feature(core_intrinsics)]` – A T Jul 22 '15 at 03:55
  • 26
    `std::any::type_name` is stable since rust 1.38: https://stackoverflow.com/a/58119924/ – Tim Robinson Jan 02 '20 at 13:13
  • 5
    Getting the type of something at compile/runtime has valid use cases. For serialization for example - or simply for debugging purposes. Those who write "You should never do such a thing" simply never ran into those use cases themselves yet. – BitTickler Jan 12 '20 at 23:08
81

If you know all the types beforehand, you can use traits to add a type_of method:

trait TypeInfo {
    fn type_of(&self) -> &'static str;
}

impl TypeInfo for i32 {
    fn type_of(&self) -> &'static str {
        "i32"
    }
}

impl TypeInfo for i64 {
    fn type_of(&self) -> &'static str {
        "i64"
    }
}

//...

No intrisics or nothin', so although more limited this is the only solution here that gets you a string and is stable. (see Boiethios's answer) However, it's very laborious and doesn't account for type parameters, so we could...

trait TypeInfo {
    fn type_name() -> String;
    fn type_of(&self) -> String;
}

macro_rules! impl_type_info {
    ($($name:ident$(<$($T:ident),+>)*),*) => {
        $(impl_type_info_single!($name$(<$($T),*>)*);)*
    };
}

macro_rules! mut_if {
    ($name:ident = $value:expr, $($any:expr)+) => (let mut $name = $value;);
    ($name:ident = $value:expr,) => (let $name = $value;);
}

macro_rules! impl_type_info_single {
    ($name:ident$(<$($T:ident),+>)*) => {
        impl$(<$($T: TypeInfo),*>)* TypeInfo for $name$(<$($T),*>)* {
            fn type_name() -> String {
                mut_if!(res = String::from(stringify!($name)), $($($T)*)*);
                $(
                    res.push('<');
                    $(
                        res.push_str(&$T::type_name());
                        res.push(',');
                    )*
                    res.pop();
                    res.push('>');
                )*
                res
            }
            fn type_of(&self) -> String {
                $name$(::<$($T),*>)*::type_name()
            }
        }
    }
}

impl<'a, T: TypeInfo + ?Sized> TypeInfo for &'a T {
    fn type_name() -> String {
        let mut res = String::from("&");
        res.push_str(&T::type_name());
        res
    }
    fn type_of(&self) -> String {
        <&T>::type_name()
    }
}

impl<'a, T: TypeInfo + ?Sized> TypeInfo for &'a mut T {
    fn type_name() -> String {
        let mut res = String::from("&mut ");
        res.push_str(&T::type_name());
        res
    }
    fn type_of(&self) -> String {
        <&mut T>::type_name()
    }
}

macro_rules! type_of {
    ($x:expr) => { (&$x).type_of() };
}

Let's use it:

impl_type_info!(i32, i64, f32, f64, str, String, Vec<T>, Result<T,S>)

fn main() {
    println!("{}", type_of!(1));
    println!("{}", type_of!(&1));
    println!("{}", type_of!(&&1));
    println!("{}", type_of!(&mut 1));
    println!("{}", type_of!(&&mut 1));
    println!("{}", type_of!(&mut &1));
    println!("{}", type_of!(1.0));
    println!("{}", type_of!("abc"));
    println!("{}", type_of!(&"abc"));
    println!("{}", type_of!(String::from("abc")));
    println!("{}", type_of!(vec![1,2,3]));

    println!("{}", <Result<String,i64>>::type_name());
    println!("{}", <&i32>::type_name());
    println!("{}", <&str>::type_name());
}

output:

i32
&i32
&&i32
&mut i32
&&mut i32
&mut &i32
f64
&str
&&str
String
Vec<i32>
Result<String,i64>
&i32
&str

Rust Playground

phicr
  • 1,332
  • 9
  • 16
  • This answer could be broken down into two separate answers in order to avoid mixing up the two. – Prajwal Jun 28 '19 at 09:51
  • 7
    @PrajwalDhatwalia I've been thinking about what you said and I feel like I am satisfied with how the versions complement each other. The trait version shows a simplification of what the macro version is doing under the hood, making its goals clearer. The macro version, on the other hand, shows how to make the trait version more generally usable; it's not the only way to do it, but even showing that it's possible is advantageous. In summary, this could be two answers but I feel the whole is greater than the sum of its parts. – phicr Jul 30 '19 at 16:19
26

Update, original answer below

How about trait function type_name, which is useful to get type name quickly.

pub trait AnyExt {
    fn type_name(&self) -> &'static str;
}

impl<T> AnyExt for T {
    fn type_name(&self) -> &'static str {
        std::any::type_name::<T>()
    }
}

fn main(){
    let my_number = 32.90;
    println!("{}",my_number.type_name());
}

Output:

f64

Original answer

I write a macro type_of!() to debug, which is original from std dbg!().

pub fn type_of2<T>(v: T) -> (&'static str, T) {
    (std::any::type_name::<T>(), v)
}

#[macro_export]
macro_rules! type_of {
    // NOTE: We cannot use `concat!` to make a static string as a format argument
    // of `eprintln!` because `file!` could contain a `{` or
    // `$val` expression could be a block (`{ .. }`), in which case the `eprintln!`
    // will be malformed.
    () => {
        eprintln!("[{}:{}]", file!(), line!());
    };
    ($val:expr $(,)?) => {
        // Use of `match` here is intentional because it affects the lifetimes
        // of temporaries - https://stackoverflow.com/a/48732525/1063961
        match $val {
            tmp => {
                let (type_,tmp) = $crate::type_of2(tmp);
                eprintln!("[{}:{}] {}: {}",
                    file!(), line!(), stringify!($val), type_);
                tmp
            }
        }
    };
    ($($val:expr),+ $(,)?) => {
        ($($crate::type_of!($val)),+,)
    };
}

fn main(){
    let my_number = type_of!(32.90);
    type_of!(my_number);
}

Output:

[src/main.rs:32] 32.90: f64
[src/main.rs:33] my_number: f64
Simson
  • 3,373
  • 2
  • 24
  • 38
Black Marco
  • 541
  • 6
  • 5
22

UPD The following does not work anymore. Check Shubham's answer for correction.

Check out std::intrinsics::get_tydesc<T>(). It is in "experimental" state right now, but it's OK if you are just hacking around the type system.

Check out the following example:

fn print_type_of<T>(_: &T) -> () {
    let type_name =
        unsafe {
            (*std::intrinsics::get_tydesc::<T>()).name
        };
    println!("{}", type_name);
}

fn main() -> () {
    let mut my_number = 32.90;
    print_type_of(&my_number);       // prints "f64"
    print_type_of(&(vec!(1, 2, 4))); // prints "collections::vec::Vec<int>"
}

This is what is used internally to implement the famous {:?} formatter.

James Ray
  • 424
  • 3
  • 15
vbo
  • 13,583
  • 1
  • 25
  • 33
  • The stable version of this now is [std::any::type_name](https://doc.rust-lang.org/std/any/fn.type_name.html). – Jk Jensen Sep 13 '22 at 16:09
17

** UPDATE ** This has not been verified to work any time recently.

I put together a little crate to do this based off vbo's answer. It gives you a macro to return or print out the type.

Put this in your Cargo.toml file:

[dependencies]
t_bang = "0.1.2"

Then you can use it like so:

#[macro_use] extern crate t_bang;
use t_bang::*;

fn main() {
  let x = 5;
  let x_type = t!(x);
  println!("{:?}", x_type);  // prints out: "i32"
  pt!(x);                    // prints out: "i32"
  pt!(5);                    // prints out: "i32"
}
mpiccolo
  • 662
  • 5
  • 14
9

You can also use the simple approach of using the variable in println!("{:?}", var). If Debug is not implemented for the type, you can see the type in the compiler's error message:

mod some {
    pub struct SomeType;
}

fn main() {
    let unknown_var = some::SomeType;
    println!("{:?}", unknown_var);
}

(playpen)

It's dirty but it works.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
DenisKolodin
  • 13,501
  • 3
  • 62
  • 65
  • 8
    *If `Debug` is not implemented* — this is a pretty unlikely case though. One of the first things you should do for most any struct is add `#[derive(Debug)]`. I think the times where you don't want `Debug` are very small. – Shepmaster Sep 24 '15 at 20:03
  • 1
    can you explain what is happening in `println!("{:?}", unknown_var);` ?? Is it a string interpolation but why the `:?` inside the curly brackets? @DenisKolodin – Julio Marins Jan 02 '17 at 18:51
  • I provoke error. The idea to let compiler provide type info with error. I used `Debug` because it isn't implemented, but you can use `{}` as well. – DenisKolodin Jan 03 '17 at 10:06
  • 1
    Surprised this answer isn't higher. This is the best approach. – Urthor Aug 04 '22 at 11:16
7

Newly added in version 1.38 std::any::type_name

use std::any::type_name;

fn type_of<T>(_: T) -> &'static str {
    type_name::<T>()
}
fn main() {
    let x = 21;
    let y = 2.5;
    println!("{}", type_of(&y));
    println!("{}", type_of(x));
}
Tanjin Alam
  • 1,728
  • 13
  • 15
5

There's a @ChrisMorgan answer to get approximate type ("float") in stable rust and there's a @ShubhamJain answer to get precise type ("f64") through unstable function in nightly rust.

Now here's a way one can get precise type (ie decide between f32 and f64) in stable rust:

fn main() {
    let a = 5.;
    let _: () = unsafe { std::mem::transmute(a) };
}

results in

error[E0512]: cannot transmute between types of different sizes, or dependently-sized types
 --> main.rs:3:27
  |
3 |     let _: () = unsafe { std::mem::transmute(a) };
  |                           ^^^^^^^^^^^^^^^^^^^
  |
  = note: source type: `f64` (64 bits)
  = note: target type: `()` (0 bits)

Update

The turbofish variation

fn main() {
    let a = 5.;
    unsafe { std::mem::transmute::<_, ()>(a) }
}

is slightly shorter but somewhat less readable.

Antony Hatchkins
  • 31,947
  • 10
  • 111
  • 111
5

If your just wanting to know the type of your variable during interactive development, I would highly recommend using rls (rust language server) inside of your editor or ide. You can then simply permanently enable or toggle the hover ability and just put your cursor over the variable. A little dialog should come up with information about the variable including the type.

nrdxp
  • 674
  • 7
  • 12
  • This is the practical, recommended answer. However it's also very unfortunate. If I am debugging a method on a server I've ssh'd into, I'm not going to have the full collection of IDE tools available to me. I'd rather print the state of the program, at runtime, rather than let the compiler fail or such. – Urthor Aug 04 '22 at 11:14
  • Not necessarily true for terminal based editors. – nrdxp Jul 25 '23 at 20:47
4

Some other answers don't work, but I find that the typename crate works.

  1. Create a new project:

    cargo new test_typename
    
  2. Modify the Cargo.toml

    [dependencies]
    typename = "0.1.1"
    
  3. Modify your source code

    use typename::TypeName;
    
    fn main() {
        assert_eq!(String::type_name(), "std::string::String");
        assert_eq!(Vec::<i32>::type_name(), "std::vec::Vec<i32>");
        assert_eq!([0, 1, 2].type_name_of(), "[i32; 3]");
    
        let a = 65u8;
        let b = b'A';
        let c = 65;
        let d = 65i8;
        let e = 65i32;
        let f = 65u32;
    
        let arr = [1,2,3,4,5];
        let first = arr[0];
    
        println!("type of a 65u8  {} is {}", a, a.type_name_of());
        println!("type of b b'A'  {} is {}", b, b.type_name_of());
        println!("type of c 65    {} is {}", c, c.type_name_of());
        println!("type of d 65i8  {} is {}", d, d.type_name_of());
        println!("type of e 65i32 {} is {}", e, e.type_name_of());
        println!("type of f 65u32 {} is {}", f, f.type_name_of());
    
        println!("type of arr {:?} is {}", arr, arr.type_name_of());
        println!("type of first {} is {}", first, first.type_name_of());
    }
    

The output is:

type of a 65u8  65 is u8
type of b b'A'  65 is u8
type of c 65    65 is i32
type of d 65i8  65 is i8
type of e 65i32 65 is i32
type of f 65u32 65 is u32
type of arr [1, 2, 3, 4, 5] is [i32; 5]
type of first 1 is i32
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Flyq
  • 1,246
  • 2
  • 13
  • 13
  • I've followed the steps you described. As of today, `typename` does not work with variables without explicit type in the declaration. Running it with `my_number` from the question gives the following error "can't call method `type_name_of` on ambiguous numeric type `{float}`. help: you must specify a type for this binding, like `f32`" – Antony Hatchkins Aug 25 '19 at 13:43
  • I test `0.65` and it works well: `type of c 0.65 0.65 is f64`. here is my version: `rustc 1.38.0-nightly (69656fa4c 2019-07-13)` – Flyq Sep 12 '19 at 14:09
4

This is simplified version of @Boiethios answer. I have removed some '&' symbols from original solution.

fn print_type_of<T>(_: T) {
    println!("{}", std::any::type_name::<T>())
}

fn main() {
    let s = "Hello";
    let i = 42;

    print_type_of(s); // &str
    print_type_of(i); // i32
    print_type_of(main); // playground::main
    print_type_of(print_type_of::<i32>); // playground::print_type_of<i32>
    print_type_of(|| "Hi!" ); // playground::main::{{closure}}
}

View in Rust Playground

E_net4
  • 27,810
  • 13
  • 101
  • 139
  • 6
    It was in the other answer's intent not to consume the parameter so that it would be usable in subsequent parts of the code, hence the `&`s. But sure, this makes even less of a difference when the type implements `Copy` (such as in `&str`, `i32`, and function pointers). – E_net4 Jan 27 '22 at 12:17
3

Better to use this:

fn print_type_of<T>(_: &T) -> String {
    format!("{}", std::any::type_name::<T>())
}

fn main() {
    let s = &"hello world".to_string();
    let cloned_s = s.clone();
    println!("{:?}", print_type_of(&s));
    println!("{:?}", print_type_of(&cloned_s));
}

Taken inference from https://stackoverflow.com/a/29168659/6774636

abhi3700
  • 156
  • 1
  • 8
2

short story;

fn tyof<T>(_: &T) -> String {
    std::any::type_name::<T>().into()
}

long story;

trait Type {
    fn type_of(&self) -> String;
}

macro_rules! Type {
    ($($ty:ty),*) => {
        $(
            impl Type for $ty {
                fn type_of(&self) -> String {
                    stringify!($ty).into()
                }
            }
        )*
    }
}

#[rustfmt::skip]
Type!(
    u8, i8, u16, i16, u32, i32, i64, u64, i128, String, [()], (), Vec<()>, &u8, &i8, &u16, &i16, &u32, &i32, &i64, &u64, &i128, &str, &[()], &Vec<()>, &() 
    // add any struct, enum or type you want
);

macro_rules! tyof {
    ($var: expr) => {{
        $var.type_of()
    }};
}

fn main() {
    let x = "Hello world!";
    println!("{}", tyof!(x));
    // or
    println!("{}", x.type_of());

    let x = 5;
    println!("{}", tyof!(x));
    // or
    println!("{}", x.type_of());
}
1

Macro form permits an usage "everywhere" while the function need an object to be parse.

Macro form (one liner):

macro_rules! ty {($type:ty) => {std::any::type_name::<$type>()}}

Macro form formated:

macro_rules! ty {
    ($type:ty) => {
        std::any::type_name::<$type>()
    };
}

Function form (borrowing is to not destroy the parsed var):

fn type_of<T>(_: &T) -> &'static str {std::any::type_name::<T>()}
fn type_of<T>(_: &T) -> &'static str {
    std::any::type_name::<T>()
}

Example:

macro_rules! ty {($type:ty) => {std::any::type_name::<$type>()}}
fn type_of<T>(_: &T) -> &'static str {std::any::type_name::<T>()}

struct DontMater<T>(T);

impl<T: std::fmt::Debug> std::fmt::Debug for DontMater<T> {
    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        fmt.write_fmt(format_args!("DontMater<{}>({:?})", ty!(T), self.0))
    }
}

fn main() {
    type µ = [Vec<String>; 7];
    println!("{:?}", DontMater(5_usize));
    println!("{:?}", DontMater("¤"));
    println!("{}", ty!(char));
    println!("{:?}", ty!(µ));
    println!("{}", type_of(&DontMater(72_i8)));
    println!("{:?}", type_of(&15_f64));
}

Returns:

DontMater<usize>(5)
DontMater<&str>("¤")
char
"[alloc::vec::Vec<alloc::string::String>; 7]"
env_vars::DontMater<i8>
"f64"
0

I like previous answer by @Coautose very much, but in case anyone wants just the type name without the namespace, for example C instead of a::b::C, here is a modified version of the macro that appears to work as intended:

macro_rules! ty {
    ($type:ty) => {{
        let result = std::any::type_name::<$type>();
        match result.rsplit_once(':') {
            Some((_, s)) => s,
            None => result,
        }
    }};
}

Usage:

debug!("Testing type name: {}", ty!(A));

However, this doesn't work well with parameterized namespaced types like Pizza<topping::Pepperoni> prints Pepperoni>.

Westy92
  • 19,087
  • 4
  • 72
  • 54
rv.kvetch
  • 9,940
  • 3
  • 24
  • 53
0
#![feature(type_name_of_val)]
use std::any::type_name_of_val;
fn main() {
    let mut my_number = 32.90;
    println!("{}", type_name_of_val(&my_number));
}
Camio
  • 1
  • Note that this will get you error `#![feature] may not be used on the stable release channel` if you are not running nightly Rust. – Sebastian Jun 17 '23 at 17:09