我有以下几点:

let mut my_number = 32.90;

如何打印my_number的类型?

使用type和type_of不起作用。有其他方法可以打印数字的类型吗?


当前回答

宏形式允许使用“无处不在”,而函数需要一个对象来解析。

宏表单(一行):

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

形成的宏观形式:

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

函数形式(借用是为了不破坏已解析的变量):

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

例子:

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

返回:

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

其他回答

如果你事先知道所有的类型,你可以使用trait来添加type_of方法:

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

//...

没有复杂或什么都没有,所以尽管有更多的限制,这是唯一的解决方案,让你得到一个字符串,是稳定的。(参见Boiethios的回答)然而,这是非常费力的,并且没有考虑类型参数,所以我们可以……

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

让我们使用它:

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

输出:

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

生锈的操场

更新,原始答案如下

trait函数type_name如何,它对于快速获取类型名称非常有用。

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

输出:

f64

原来的答案

我写了一个宏type_of!()来调试,它来自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);
}

输出:

[src/main.rs:32] 32.90: f64
[src/main.rs:33] my_number: f64

你可以使用std::any::type_name函数。这并不需要一个夜间编译器或外部板条箱,结果是非常正确的:

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

注意:如文档中所述,此信息只能用于调试目的:

这是用于诊断用途。字符串的确切内容和格式没有指定,只是尽力描述该类型。

如果你想让你的类型表示在不同的编译器版本中保持相同,你应该使用一个trait,就像phicr的答案一样。

最好使用这个:

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

来自https://stackoverflow.com/a/29168659/6774636的推论

我非常喜欢@Coautose之前的回答,但如果有人只想要没有名称空间的类型名称,例如C而不是a::b::C,这里是一个修改后的宏版本,看起来像预期的那样工作:

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

用法:

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