ChatGPT解决这个技术问题 Extra ChatGPT

How do I print 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?


B
Boiethios

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.


best answer for me, as most devs want to use this for debugging purposes, like printing parsing failures
see also std::any::type_name_of_val
S
Shepmaster

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:

What is the {integer} or {float} in a compiler error message?

¹ 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.


:? 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.
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?
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?
@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.
this sounds very much like a hack. is this actually the idiomatic way to check the type of a variable?
C
Chris Morgan

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

@vbo: not until it’s stabilised. Something like this is unlikely to be stabilised for quite some time, if ever—and it wouldn’t surprise me if it is never stabilised; it’s not the sort of thing that you should ever really do.
On rust-nightly (1.3) it only worked when changing that first line to #![feature(core_intrinsics)]
@DmitriNesteruk: print_type_of is taking references (&T), not values (T), so you must pass &&str rather than &str; that is, print_type_of(&"foo") rather than print_type_of("foo").
std::any::type_name is stable since rust 1.38: stackoverflow.com/a/58119924
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.
p
phicr

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


This answer could be broken down into two separate answers in order to avoid mixing up the two.
@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.
J
James Ray

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.


S
Simson

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

m
mpiccolo

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

@vbo says his solution does not work anymore. Does yours work?
not working ` error[E0554]: #![feature] may not be used on the stable release channel `
Can't talk about stable, but on nightly it (still) works.
S
Shepmaster

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.


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.
can you explain what is happening in println!("{:?}", unknown_var); ?? Is it a string interpolation but why the :? inside the curly brackets? @DenisKolodin
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.
A
Antony Hatchkins

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.


If you already know it is float, telling between f32 and f64 can be accomplished with std::mem::size_of_val(&a)
S
Shepmaster

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

Create a new project: cargo new test_typename Modify the Cargo.toml [dependencies] typename = "0.1.1" Modify your source code use typename::TypeName; fn main() { assert_eq!(String::type_name(), "std::string::String"); assert_eq!(Vec::::type_name(), "std::vec::Vec"); 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

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"
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)
n
nrdxp

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.


E
E_net4 - Mr Downvoter

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


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).
C
Coautose Synesh

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"