简体   繁体   中英

How do I print in Rust the type of a variable?

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?

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.

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 .

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"
}

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

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.

** 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"
}

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.

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

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.

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

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.

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

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"

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));

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());
}

You can use std::any::type_name . The following are examples of primitive data types which are capiable without & .

use std::any::type_name;

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

fn main() {
    let str1 = "Rust language";
    let str2 = str1;
    println!("str1 is:  {}, and the type is {}.", str1, type_of(str1));
    println!("str2 is: {}, and the type is {}.", str2, type_of(str2));
    let bool1 = true;
    let bool2 = bool1;
    println!("bool1 is {}, and the type is {}.", bool1, type_of(bool1));
    println!("bool2 is {}, and the type is {}.", bool2, type_of(bool2));
    let x1 = 5;
    let x2 = x1;
    println!("x1 is {}, and the type is {}.", x1, type_of(x1));
    println!("x2 is {}, and the type is {}.", x2, type_of(x2));
    let a1 = 'a';
    let a2 = a1;
    println!("a1 is {}, and the type is {}.", a1, type_of(a1));
    println!("a2 is {}, and the type is {}.", a2, type_of(a2));
    let tup1= ("hello", 5, 'c');
    let tup2 = tup1;
    println!("tup1 is {:?}, and the type is {}.", tup1, type_of(tup1));
    println!("tup2 is {:?}, and the type is {}.", tup2, type_of(tup2));
    let array1: [i32; 3] = [0; 3];
    let array2 = array1;
    println!("array1 is {:?}, and the type is {}.", array1, type_of(array1));
    println!("array2 is {:?}, and the type is {}.", array2, type_of(array2));
    let array: [i32; 5] = [0, 1, 2, 3, 4];
    let slice1 = &array[0..3];
    let slice2 = slice1;
    println!("slice1 is {:?}, and the type is {}.", slice1, type_of(slice1));
    println!("slice2 is {:?}, and the type is {}.", slice2, type_of(slice2));
}

The output is

str1 is:  Rust language, and the type is &str.
str2 is: Rust language, and the type is &str.
bool1 is true, and the type is bool.
bool2 is true, and the type is bool.
x1 is 5, and the type is i32.
x2 is 5, and the type is i32.
a1 is a, and the type is char.
a2 is a, and the type is char.
tup1 is ("hello", 5, 'c'), and the type is (&str, i32, char).
tup2 is ("hello", 5, 'c'), and the type is (&str, i32, char).
array1 is [0, 0, 0], and the type is [i32; 3].
array2 is [0, 0, 0], and the type is [i32; 3].
slice1 is [0, 1, 2], and the type is &[i32].
slice2 is [0, 1, 2], and the type is &[i32].

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM