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


237

我有以下几点:

let mut my_number = 32.90;

如何打印的类型my_number

使用typetype_of无效。还有其他打印号码类型的方法吗?

Answers:


177

如果仅希望找出变量的类型并愿意在编译时进行操作,则可能会导致错误并让编译器选择它。

例如,将变量设置为无效的类型

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

调用无效的方法

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

访问无效的字段

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

这些揭示了类型,在这种情况下实际上并没有完全解决。在第一个示例中将其称为“浮点变量”,{float}在所有三个示例中均将其称为“” 。这是部分解决的类型可能会最终f32还是f64取决于你如何使用它。“ {float}”不是合法的类型名称,它是一个占位符,表示“我不确定这是什么”,但这一个浮点数。对于浮点变量,如果不对其进行限制,则默认为f64¹。(无限定的整数文字将默认为i32。)

也可以看看:


¹仍然存在使编译器感到困惑的方法,以致于无法在f32和之间进行选择f64;我不确定。它曾经像一样简单32.90.eq(&32.90),但是f64现在和现在都很好,所以我不知道。


4
:?现在已经有相当长的时间是手动执行的。但更重要的std::fmt::Debug:?,数字类型的实现(为此使用的是)不再包含后缀以表明其属于哪种类型。
克里斯·摩根

2
我经常使用这些技术来尝试查找表达式的类型,但是它并不总是有效,特别是当涉及类型参数时。例如,编译器将告诉我,ImageBuffer<_, Vec<_>>当我尝试编写将这些内容之一作为参数的函数时,期望它对我没有太大帮助。这会在代码中发生,否则我会编译直到添加:()。有没有更好的办法?
Christopher Armstrong

2
这似乎有点令人费解且不直观。对于代码编辑器,例如当光标停在变量上时,像许多其他语言一样,Emacs提供类型将非常困难吗?如果编译器可以在出错时告诉类型,那么当没有任何错误时,它肯定也应该已经知道类型了吗?
xji

1
@JIXiang:Rust Language Server旨在将这些信息提供给IDE,但还不成熟-它的第一个Alpha版本是几天前的事情。是的,这是一种古怪的方法。是的,越来越少的达到目标的神秘方法正在逐渐出现。
克里斯·摩根

1
这听起来很像黑客。这实际上是检查变量类型的惯用方式吗?
confused00

109

有一个不稳定的函数std::intrinsics::type_name可以为您提供类型的名称,尽管您必须每晚使用Rust构建(这在稳定的Rust中不太可能起作用)。这是一个例子:

#![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:直到稳定为止。像这样的事情在很长一段时间内都不太可能稳定下来,而且如果永不稳定,也不会令我感到惊讶。这不是您真正应该做的事情。
克里斯·摩根

2
在rust-night(1.3)上,它仅在将第一行更改为#![feature(core_intrinsics)]
AT

1
@DmitriNesteruk:print_type_of正在使用引用(&T),而不是值(T),因此您必须通过&&str而不是&str; 即,print_type_of(&"foo")而不是print_type_of("foo")
克里斯·摩根

您是对的,已经过去了3年,现在仍然不稳定。
安东·科奇科夫'18

5
std::any::type_name自锈蚀1.38起是稳定的:stackoverflow.com/a/58119924
蒂姆·罗宾逊

66

您可以使用该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的answer所示


1
对我来说,这是最好的答案,因为大多数开发人员都希望将其用于调试目的,例如打印解析失败
kaiser

正是我需要的,我不知道为什么这不是标记的答案!
James Poulose

1
@JamesPoulose因为此功能是最近的,所以我的回答是较新的。
Boiethios

53

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

锈操场


为了避免混淆这两个答案,可以将这个答案分解为两个单独的答案。
Prajwal Dhatwalia

2
@PrajwalDhatwalia我一直在思考您说的内容,我对这些版本之间的互补感到满意。特质版本简化了宏版本在幕后所做的工作,使其目标更加清晰。另一方面,宏版本显示了如何使特征版本更通用。这不是唯一的方法,但是即使证明它是可能的也是有利的。总而言之,这可能是两个答案,但我认为整体大于各个部分的总和。
phicr '19

19

UPD以下内容不再起作用。检查Shubham的答案以进行更正。

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

这是内部用于实现著名{:?}格式器的内容。


15

**更新**最近尚未验证此功能可以正常工作。

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

@vbo说他的解决方案不再起作用。你的工作吗?
安东尼·哈奇金斯

无法正常工作`error [E0554]:#![feature]可能无法在稳定的发布频道上使用
Muhammed Moussa

7

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

mod some {
    pub struct SomeType;
}

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

围栏

它很脏但是可以用。


8
如果Debug未实施 -这是极不可能的情况。对于大多数结构,您应该做的第一件事就是add #[derive(Debug)]。我认为您不想要的时间Debug很少。
Shepmaster

1
你能解释一下发生了什么println!("{:?}", unknown_var);吗?是字符串插值,但为什么:?在花括号内?@DenisKolodin
Julio Marins

我惹错了。让编译器提供错误类型信息的想法。我Debug之所以使用它,是因为它尚未实现,但您也可以使用{}
DenisKolodin'1

4

有一个@ChrisMorgan 答案可以在稳定的锈蚀中获得近似类型(“ float”),还有一个@ShubhamJain 答案可以通过夜间锈蚀的不稳定函数获得精确的类型(“ 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) }
}

略短一些,但可读性较差。


如果您已经知道了float,就可以进行交流,f32并且f64可以完成std::mem::size_of_val(&a)
Antony Hatchkins

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

我已按照您描述的步骤进行。从今天开始,typename不适用于声明中没有显式类型的变量。my_number 从问题中运行它会出现以下错误“无法type_name_of在模糊数字类型上调用方法{float}。帮助:您必须为此绑定指定一种类型,例如f32
Antony Hatchkins

我测试了 0.65,效果很好:type of c 0.65 0.65 is f64。这是我的版本:rustc 1.38.0-nightly (69656fa4c 2019-07-13)
Flyq

1

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.