Как напечатать тип переменной в Rust?

У меня есть следующее:

let mut my_number = 32.90;

Как мне напечатать тип my_number?

Использование type и type_of не сработало. Есть ли другой способ, которым я могу напечатать тип номера?

Ответ 1

Если вы просто хотите узнать тип переменной и хотите сделать это во время компиляции, вы можете вызвать ошибку и заставить компилятор ее забрать.

Например, установите для переменной тип, который не работает:

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 does not have fields
 --> src/main.rs:3:15
  |
3 |     my_number.what_is_this
  |               ^^^^^^^^^^^^

Они раскрывают тип, который в данном случае фактически не полностью разрешен. Она называется "переменная с плавающей точкой" в первом примере и "{float}" во всех трех примерах; это частично разрешенный тип, который может закончиться f32 или f64, в зависимости от того, как вы его используете. "{float}" не является допустимым именем типа, его заполнитель означает "Я не совсем уверен, что это такое", но это число с плавающей запятой. В случае переменных с плавающей запятой, если вы не ограничите их, по умолчанию будет f64 ¹. (Неполный целочисленный литерал по умолчанию будет i32.)

Смотрите также:


Still Могут существовать способы сбить с толку компилятор, чтобы он не мог выбрать между f32 и f64; Я не уверен. Раньше он был таким же простым, как 32.90.eq(&32.90), но теперь он воспринимается как f64 и радостно пыхтит, поэтому я не знаю.

Ответ 2

Существует нестабильная функция std::intrinsics::type_name, которая может дать вам имя типа, хотя вам придется использовать ночную сборку Rust (это вряд ли когда-либо работали в стабильной ржавчине). Вот пример:

#![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"
}

Ответ 3

Если вы заранее знаете все типы, вы можете использовать черты для добавления метода 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

Ответ 4

UPD Следующее больше не работает. Проверьте ответ 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>"
}

Это то, что используется внутри для реализации известного форматера {:?}.

Ответ 5

Я собрал немного корзины, чтобы сделать это, основываясь на ответе 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"
}

Ответ 6

Вы также можете использовать простой подход использования переменной в println!("{:?}", var). Если Debug не реализована для типа, вы можете увидеть тип в сообщении об ошибке компилятора:

mod some {
    pub struct SomeType;
}

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

(детский манеж)

Это грязно, но это работает.

Ответ 7

Есть ответ @ChrisMorgan answer, чтобы получить приблизительный тип ("float") в стабильной ржавчине, и ответ @ShubhamJain answer, чтобы получить точный тип ("f64") через нестабильную функцию в ночной ржавчине.

Теперь здесь можно получить точный тип (т.е. выбрать между 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) }
}

немного короче, но несколько менее читабельно.

Ответ 8

Вы можете использовать функцию std::any::type_name.

Вам больше не нужен ночной компилятор или внешний ящик.

Ответ 9

Некоторые другие ответы не работают, но я обнаружил, что ящик с типом работает.

  1. Создайте новый проект:

    cargo new test_typename
    
  2. Изменить Cargo.toml

    [dependencies]
    typename = "0.1.1"
    
  3. Изменить ваш исходный код

    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

Ответ 10

Вы можете проверить ящик typename.

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]");
}

Это требует, чтобы вы получили черту для пользовательских типов.

Ответ 11

Если вы просто хотите узнать тип переменной во время интерактивной разработки, я настоятельно рекомендую использовать rls (сервер языка ржавчины) внутри вашего редактора или ide. Затем вы можете просто включить или отключить возможность наведения и просто навести курсор на переменную. В небольшом диалоге должна появиться информация о переменной, включая тип.