如何在Rust中打印变量的类型?

时间:2014-02-13 06:49:09

标签: types rust

我有以下内容:

let mut my_number = 32.90;

如何打印my_number的类型?

使用typetype_of无效。还有其他方法可以打印数字的类型吗?

12 个答案:

答案 0 :(得分:140)

如果你只想找出变量的类型并且愿意在编译时这样做,你可能会导致错误并让编译器接收它。

例如,set the variable to a type which doesn't worklet () = x;也可以):

error[E0308]: mismatched types
 --> <anon>:2:29
  |
2 |     let mut my_number: () = 32.90;
  |                             ^^^^^ expected (), found floating-point variable
  |
  = note: expected type `()`
  = note:    found type `{float}`

error: aborting due to previous error

或者在大多数情况下call an invalid methodget an invalid field

error: no method named `what_is_this` found for type `{float}` in the current scope
 --> <anon>:3:15
  |
3 |     my_number.what_is_this();
  |               ^^^^^^^^^^^^

error: aborting due to previous error
error: attempted access of field `what_is_this` on type `{float}`, but no field with that name was found
 --> <anon>:3:5
  |
3 |     my_number.what_is_this
  |     ^^^^^^^^^^^^^^^^^^^^^^

error: aborting due to previous error

这些揭示了这种情况,在这种情况下实际上并没有完全解决。它在第一个例子中称为“浮点变量”,在所有三个例子中称为“{float}”;这是部分解析的类型,可能会以f32f64结束,具体取决于您使用它的方式。 “{float}”不是合法类型名称,它是占位符,意思是“我不完全确定这是什么”,但它一个浮点数。对于浮点变量,如果不限制它,它将默认为f64¹。 (非限定整数文字将默认为i32。)


¹可能仍有一些方法让编译器感到困惑,因此它无法在f32f64之间作出决定;我不确定。它曾经像32.90.eq(&32.90)一样简单,但是现在把它们都当作f64对待并快乐地开始,所以我不知道。

答案 1 :(得分:87)

有一个不稳定的函数std::intrinsics::type_name,它可以为你提供一个类型的名称,虽然你必须使用每晚构建的Rust(这不太可能在稳定的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"
}

答案 2 :(得分:42)

如果您事先了解所有类型,则可以使用特征添加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"
    }
}

//...

没有内在或没什么',所以虽然更有限,但这是唯一能让你获得字符串且稳定的解决方案。但是,它非常费力且不考虑类型参数,所以我们可以...

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

Rust Playground

答案 3 :(得分:25)

您可以使用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}}
}

答案 4 :(得分:18)

UPD 以下内容不再有效。检查Shubham's answer是否有更正。

结帐std::intrinsics::get_tydesc<T>()。它现在处于“实验”状态,但如果你只是在类型系统中乱砍,那就没关系。

查看以下示例:

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

这是实现着名的{:?}格式化程序的used internally

答案 5 :(得分:9)

根据vbo的回答,我把一个小箱子放在一起做这件事。它为您提供了一个返回或打印出类型的宏。

将它放在Cargo.toml文件中:

[dependencies]
t_bang = "0.1.2"

然后你可以像这样使用它:

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

答案 6 :(得分:6)

您还可以使用println!("{:?}", var)中使用变量的简单方法。如果没有为类型实现Debug,您可以在编译器的错误消息中看到类型:

mod some {
    pub struct SomeType;
}

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

playpen

它很脏但是有效。

答案 7 :(得分:4)

有一个@ChrisMorgan answer可以在稳定的锈蚀中获得近似类型(“ float”),还有一个@ShubhamJain answer可以通过夜间锈蚀的不稳定函数获得精确的类型(“ f64”)。

现在这是一种可以在稳定的锈蚀中获得精确类型(即在f32和f64之间确定)的方法:

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

产生

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)

更新

turbo鱼变种

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

稍短,但可读性较差。

答案 8 :(得分:4)

更新,下面的原始答案

trait function 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

答案 9 :(得分:1)

其他一些答案无效,但我发现typename条板箱有效。

  1. 创建一个新项目:

    cargo new test_typename
    
  2. 修改Cargo.toml

    [dependencies]
    typename = "0.1.1"
    
  3. 修改您的源代码

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

输出为:

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

答案 10 :(得分:1)

如果您只是想在交互式开发过程中知道变量的类型,我强烈建议您在编辑器或ide中使用rls(锈语言服务器)。然后,您可以简单地永久启用或切换悬停功能,只需将光标放在变量上即可。一个小对话框应该显示有关变量的信息,包括类型。

答案 11 :(得分:-3)

您可以使用std::any::type_name。以下是不使用&即可使用的原始数据类型的示例。

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

输出为

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