如何在 Rust 中打印变量的类型?
Posted
技术标签:
【中文标题】如何在 Rust 中打印变量的类型?【英文标题】:How do I print the type of a variable? 【发布时间】:2014-03-11 21:53:21 【问题描述】:我有以下几点:
let mut my_number = 32.90;
如何打印my_number
的类型?
使用type
和type_of
不起作用。还有其他方法可以打印号码的类型吗?
【问题讨论】:
【参考方案1】:更新,下面的原始答案
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!()
来调试,原来是stddbg!()
。
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://***.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
【讨论】:
【参考方案2】:如果你事先知道所有的类型,你可以使用traits添加一个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's answer)但是,它非常费力并且不会考虑类型参数,所以我们可以...
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
【讨论】:
这个答案可以分成两个单独的答案,以避免混淆两者。 @PrajwalDhatwalia 我一直在考虑你所说的,我觉得我对这些版本如何相互补充感到满意。 trait 版本简化了宏版本在幕后所做的事情,使其目标更加清晰。另一方面,宏版本展示了如何使 trait 版本更普遍可用;这不是唯一的方法,但即使表明它是可能的也是有利的。总之,这可能是两个答案,但我觉得整体大于部分之和。【参考方案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].
【讨论】:
这不会向existing answer usingstd::any::type_name
添加任何内容
这看起来像是一个重复的答案,您可能以前没有见过另一个答案。感谢您的帮助。【参考方案4】:
** 更新 ** 这在最近的任何时候都没有经过验证。
我根据 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 说他的解决方案不再起作用。你的有用吗? 不工作`错误[E0554]:#![feature]
可能无法在稳定发布频道上使用`
不能谈论稳定,但在夜间它(仍然)有效。【参考方案5】:
您可以使用std::any::type_name
函数。这不需要 nightly 编译器或外部 crate,结果非常正确:
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
请注意:如文档中所述,此信息只能用于调试目的:
这是用于诊断用途。没有指定字符串的确切内容和格式,只是对类型的尽力描述。
如果你希望你的类型表示在编译器版本之间保持不变,你应该使用一个特征,比如phicr's answer。
【讨论】:
对我来说最好的答案,因为大多数开发人员都希望将其用于调试目的,例如打印解析失败 另见 std::any::type_name_of_val【参考方案6】:如果您只是想在交互式开发过程中了解变量的类型,我强烈建议您在编辑器或 ide 中使用rls(rust 语言服务器)。然后,您可以简单地永久启用或切换悬停功能,只需将光标放在变量上即可。一个小对话框应该会提供有关变量的信息,包括类型。
【讨论】:
【参考方案7】:@ChrisMorgan answer 在稳定的 rust 中获得近似类型(“float”),@ShubhamJain answer 在夜间 rust 中通过不稳定的函数获得精确类型(“f64”)。
现在有一种方法可以在稳定的 rust 中获得精确的类型(即在 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)
更新
涡轮鱼变种
fn main()
let a = 5.;
unsafe std::mem::transmute::<_, ()>(a)
略短,但可读性稍差。
【讨论】:
如果你已经知道它是float
,那么f32
和f64
之间的区分可以通过std::mem::size_of_val(&a)
来完成【参考方案8】:
如果您只想找出变量的类型并愿意在编译时进行,您可能会导致错误并让编译器拾取它。
例如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`
或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();
| ^^^^^^^^^^^^
或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
| ^^^^^^^^^^^^
这些揭示了类型,在这种情况下实际上并没有完全解决。在第一个示例中称为“浮点变量”,在所有三个示例中都称为“float
”;这是一个部分解析的类型,最终可能是f32
或f64
,具体取决于您如何使用它。 “float
”不是一个合法的类型名称,它是一个占位符,意思是“我不完全确定这是什么”,但它是一个浮点数。在浮点变量的情况下,如果不对其进行约束,它将默认为f64
¹。 (不合格的整数文字将默认为i32
。)
另见:
What is the integer or float in a compiler error message?¹ 可能仍有一些方法让编译器感到困惑,使其无法在f32
和f64
之间做出决定;我不知道。它曾经像32.90.eq(&32.90)
一样简单,但现在它把两者都当作f64
并愉快地前进,所以我不知道。
【讨论】:
:?
已经手动实现了很长时间。但更重要的是,数字类型的 std::fmt::Debug
实现(因为这是 :?
使用的)不再包含表示它属于哪种类型的后缀。
我经常使用这些技术来尝试查找表达式的类型,但它并不总是有效,尤其是在涉及类型参数时。例如,编译器会告诉我它期待一个ImageBuffer<_, Vec<_>>
,当我尝试编写一个将这些东西之一作为参数的函数时,这对我没有多大帮助。这发生在否则会编译的代码中,直到我添加:()
。有没有更好的办法?
这似乎有点复杂和不直观。代码编辑器是否会非常困难,例如当光标停留在变量上时,Emacs 提供类型,就像在许多其他语言中一样?如果编译器可以在错误时告诉类型,那么它肯定也应该在没有任何错误时已经知道类型?
@JIXIang:Rust 语言服务器就是为 IDE 提供这些信息,但它还不成熟——它的第一个 alpha 版本才几天前。是的,这是一种可怕的方法;是的,实现目标的不那么深奥的方法正在稳步出现。
这听起来很像 hack。这实际上是检查变量类型的惯用方法吗?【参考方案9】:
其他一些答案不起作用,但我发现 typename 板条箱有效。
创建一个新项目:
cargo new test_typename
修改 Cargo.toml
[dependencies]
typename = "0.1.1"
修改你的源代码
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
运行它会出现以下错误“无法在不明确的数字类型float
上调用方法type_name_of
。帮助:您必须为此绑定指定一个类型,例如f32
”
我测试了0.65
,它运行良好:type of c 0.65 0.65 is f64
。这是我的版本:rustc 1.38.0-nightly (69656fa4c 2019-07-13)
【参考方案10】:
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 来实现著名的:?
格式化程序。
【讨论】:
【参考方案11】:有一个不稳定的函数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"
【讨论】:
@vbo:在稳定之前不要。如果有的话,这样的事情不太可能在相当长的一段时间内稳定下来——如果它从未稳定下来,我也不会感到惊讶;这不是你真正应该做的事情。 在 rust-nightly (1.3) 上,它仅在将第一行更改为#![feature(core_intrinsics)]
时才有效
@DmitriNesteruk: print_type_of
正在获取引用 (&T
),而不是值 (T
),因此您必须通过 &&str
而不是 &str
;也就是说,print_type_of(&"foo")
而不是 print_type_of("foo")
。
std::any::type_name
自 rust 1.38 起稳定:***.com/a/58119924
在编译/运行时获取某物的类型具有有效的用例。例如,用于序列化 - 或仅用于调试目的。那些写“你永远不应该做这样的事情”的人自己根本就没有遇到过这些用例。【参考方案12】:
您也可以使用println!(":?", var)
中的变量的简单方法。如果该类型没有实现Debug
,则可以在编译器的错误信息中看到该类型:
mod some
pub struct SomeType;
fn main()
let unknown_var = some::SomeType;
println!(":?", unknown_var);
(playpen)
它很脏,但它可以工作。
【讨论】:
如果Debug
没有实现——不过这种情况不太可能发生。对于大多数结构,您应该做的第一件事就是添加#[derive(Debug)]
。我认为你不想要Debug
的时间非常少。
你能解释一下println!(":?", unknown_var);
发生了什么吗?它是字符串插值,但为什么 :?
在大括号内? @DenisKolodin
我引发了错误。让编译器提供错误类型信息的想法。我使用了Debug
,因为它没有实现,但你也可以使用
。以上是关于如何在 Rust 中打印变量的类型?的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Rust 中打印变量并让它显示有关该变量的所有内容,例如 Ruby 的 .inspect?