【发布时间】:2023-03-26 07:53:01
【问题描述】:
我有以下几点:
let mut my_number = 32.90;
如何打印my_number的类型?
使用type 和type_of 不起作用。还有其他方法可以打印号码的类型吗?
【问题讨论】:
我有以下几点:
let mut my_number = 32.90;
如何打印my_number的类型?
使用type 和type_of 不起作用。还有其他方法可以打印号码的类型吗?
【问题讨论】:
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://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
【讨论】:
如果你事先知道所有的类型,你可以使用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
【讨论】:
您可以使用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].
【讨论】:
** 更新 ** 这在最近的任何时候都没有经过验证。
我根据 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]可能无法在稳定发布频道上使用`
您可以使用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。
【讨论】:
如果您只是想在交互式开发过程中了解变量的类型,我强烈建议您在编辑器或 ide 中使用rls(rust 语言服务器)。然后,您可以简单地永久启用或切换悬停功能,只需将光标放在变量上即可。一个小对话框应该会提供有关变量的信息,包括类型。
【讨论】:
@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)来完成
如果您只想找出变量的类型并愿意在编译时进行,您可能会导致错误并让编译器拾取它。
例如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}`
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 实现(因为这是 :? 使用的)不再包含表示它属于哪种类型的后缀。
ImageBuffer<_, Vec<_>>,当我尝试编写一个将这些东西之一作为参数的函数时,这对我没有多大帮助。这发生在否则会编译的代码中,直到我添加:()。有没有更好的办法?
其他一些答案不起作用,但我发现 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)
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 来实现著名的{:?} 格式化程序。
【讨论】:
有一个不稳定的函数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"
}
【讨论】:
#![feature(core_intrinsics)] 时才有效
print_type_of 正在获取引用 (&T),而不是值 (T),因此您必须通过 &&str 而不是 &str;也就是说,print_type_of(&"foo") 而不是 print_type_of("foo")。
std::any::type_name 自 rust 1.38 起稳定:stackoverflow.com/a/58119924
您也可以使用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,因为它没有实现,但你也可以使用{}。