我有以下内容:
let mut my_number = 32.90;
我该如何打印my_number
的类型?
使用type
和type_of
都无法实现。是否有其他方法可以打印出该数字的类型?
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}}
}
请注意:文档中已经说明,此信息仅限于调试目的使用:
这是用于诊断目的的。字符串的确切内容和格式没有指定,除了尽力描述类型。
如果您希望在编译器版本之间保持类型表示方式不变,您应该使用特性,比如在phicr的答案中所示。
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
,并且可以正常运行,所以我不知道。std::fmt::Debug
实现(也就是“:?”使用的)不再包含后缀来指示它属于哪种类型。 - Chris MorganImageBuffer<_, Vec<_>>
,但当我试图编写一个以这些内容作为参数的函数时,这并没有帮助我太多。而且这种情况通常发生在能够正常编译的代码中,直到我加上 :()
为止。难道没有更好的方法吗? - Christopher Armstrong有一个不稳定的函数std::intrinsics::type_name
,可以获取类型名称,但必须使用 Rust nightly 版本(这在稳定版本中几乎不可能工作)。这里有一个例子:
#![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"
}
#![feature(core_intrinsics)]
时才能正常工作。 - A Tstd::any::type_name
自 Rust 1.38 版本以来已经稳定:https://dev59.com/xGEi5IYBdhLWcg3wCYGY#58119924/ - Tim Robinsontype_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
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!()
来调试,该宏源自于标准库的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://dev59.com/71YM5IYBdhLWcg3whAaH#48732525
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
更新 下面的方法已经不再适用。请查看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>"
}
这是用于实现著名的 {:?}
格式化程序的内部使用内容,请参考链接。
** 更新 ** 最近没有验证其有效性。
我根据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"
}
#![feature]
不能在稳定版发布通道上使用。 - Muhammed Moussa版本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));
}
您也可以采用简单的方法,在println!("{:?}", var)
中使用变量。如果该类型未实现Debug
,则可以在编译器的错误消息中看到该类型:
mod some {
pub struct SomeType;
}
fn main() {
let unknown_var = some::SomeType;
println!("{:?}", unknown_var);
}
(playpen)
虽然有点脏,但确实管用。
Debug
——虽然这种情况可能性很小。对于大多数结构体,你应该做的第一件事就是添加#[derive(Debug)]
。我认为不需要使用Debug
的情况非常少。 - Shepmasterprintln!("{:?}", unknown_var);
发生了什么吗?这是字符串插值,但为什么花括号里面有 :?
?@DenisKolodin - Julio MarinsDebug
,因为它还没有被实现,但你也可以使用 {}
。 - DenisKolodin如果你只是想在交互式开发中知道你的变量类型,我强烈建议在你的编辑器或IDE中使用rls(rust语言服务器)。然后你可以简单地永久启用或切换悬停功能,将光标放在变量上方。一个小对话框应该会出现,并提供关于变量的信息,包括变量类型。
print_type_of(&a[1..]);
- somenickname