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

458

我有以下内容:

let mut my_number = 32.90;

我该如何打印my_number的类型?

使用typetype_of都无法实现。是否有其他方法可以打印出该数字的类型?

18个回答

5

有一个@ChrisMorgan的回答介绍了如何在稳定版Rust中获取近似类型("float"),还有一个@ShubhamJain的回答介绍了如何通过不稳定的函数在夜版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”,那么可以使用“std::mem::size_of_val(&a)”来区分“f32”和“f64”。 - Antony Hatchkins

4

这是@Boiethios答案的简化版本。我从原始方案中删除了一些'&amp;'符号。

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查看


6
另一个回答的意图是不消耗参数,以便在代码的后续部分中可以使用它,因此使用了&符号。但当类型实现了Copy时(例如&stri32和函数指针),这甚至会使差异更小。 - E net4

4

有些其他的答案不可行,但我发现typename库可以使用。

  1. Create a new project:

    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());
    }
    
输出结果为:
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”。 - Antony Hatchkins
我测试了 0.65 并且结果良好:c 的类型为 0.65 0.65 是 f64。 这是我的版本:rustc 1.38.0-nightly (69656fa4c 2019-07-13) - Flyq

4
最好使用这个:
fn print_type_of<T>(_: &T) -> String {
    format!("{}", std::any::type_name::<T>())
}

fn main() {
    let s = &"hello world".to_string();
    let cloned_s = s.clone();
    println!("{:?}", print_type_of(&s));
    println!("{:?}", print_type_of(&cloned_s));
}

参考自https://dev59.com/xGEi5IYBdhLWcg3wCYGY#29168659

,推理如下:


2

短篇小说;

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
宏形式允许在任何地方使用,而函数需要解析对象。
宏形式(一行代码):
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"

0
#![feature(type_name_of_val)]
use std::any::type_name_of_val;
fn main() {
    let mut my_number = 32.90;
    println!("{}", type_name_of_val(&my_number));
}

1
请注意,如果您没有使用夜间版的Rust,这将导致错误“#![feature]不可在稳定版本通道上使用”。 - Sebastian

0
我非常喜欢@Coautose之前的回答,但是如果有人只想要类型名称而不带命名空间,例如a::b::C变成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));

然而,这在参数化的命名空间类型上不起作用,比如 Pizza<topping::Pepperoni> 会打印出 Pepperoni>

巧妙地使用 rsplit - MEMark

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接