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

我有以下几点:

let mut my_number = 32.90;

如何打印my_number的类型?

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

163100 次浏览

如果你只是想找出变量的类型,并愿意在编译时这样做,你可以导致一个错误,并让编译器拾取它。

例如,将变量设置为不起作用的类型:

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}”;这是一个部分解析的类型,最终可能是f32f64,这取决于你如何使用它。" {float} "不是一个合法的类型名称,它是一个占位符,意思是“我不完全确定这是什么”,但它是一个浮点数。在浮点变量的情况下,如果不约束它,它将默认为f64¹。(非限定整数字面值默认为i32。)

参见:


¹可能仍然有一些方法使编译器困惑,使它无法在f32f64之间做出决定;我不确定。它曾经像32.90.eq(&32.90)一样简单,但现在将两者都视为f64并愉快地进行,所以我不知道。

乌利希期刊指南以下不再工作。检查Shubham的回答进行更正。

查看std::intrinsics::get_tydesc<T>()。它现在处于“实验”状态,但如果您只是对类型系统进行了修改,那么它是OK的。

请看下面的例子:

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

这就是用来实现著名的{:?}格式化程序的在内部使用

现在有稳定的函数std::any::type_name(自Rust 1.38起)——以前这个答案推荐不稳定的函数std::intrinsics::type_name,尽管你必须使用Rust的夜间构建——它可以获得类型的名称。这里有一个例子:

fn print_type_of<T>(_: &T) {
println!("{}", std::any::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"
}

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

mod some {
pub struct SomeType;
}


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

(游戏围栏)

虽然很脏,但很管用。

**更新**最近没有被验证工作。

我把一个小板条箱一起做这个基于vbo的答案。它提供了一个宏来返回或打印类型。

把这个放在你的货物里。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"
}

如果你事先知道所有类型,你可以使用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

Rust Playground .

其他一些答案不工作,但我发现typename板条箱工作。

  1. 创建一个新项目:

    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

有一个@ChrisMorgan 回答在稳定rust中获得近似类型(“float”),有一个@ShubhamJain 回答在夜间rust中通过不稳定函数获得精确类型(“f64”)。

现在有一种方法可以得到精确的类型(即在f32和f64之间决定)在稳定的rust:

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)

更新

涡轮鱼的变异

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

略短,但可读性稍差。

你可以使用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的回答

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

更新以下是原始答案

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

这是@Boiethios回答的简化版本。我已经从原来的解决方案中删除了一些“&”符号。

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

Rust Playground视图

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

宏表单(一行):

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"

我非常喜欢@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));

短篇小说;

fn tyof<T>(_: &T) -> String {
std::any::type_name::<T>().into()
}

很长的故事;

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

1.38版新增std::any::type_name

use std::any::type_name;


fn type_of<T>(_: T) -> &'static str {
type_name::<T>()
}
fn main() {
let x = 21;
let y = 2.5;
println!("{}", type_of(&y));
println!("{}", type_of(x));
}