Come posso stampare il tipo di una variabile in Rust?

Ho il seguente:

let mut my_number = 32.90; 

Come posso stampare il tipo di my_number ?

L’uso di type e type_of non ha funzionato. C’è un altro modo in cui posso stampare il tipo del numero?

Se desideri semplicemente scoprire il tipo di una variabile e sei disposto a farlo in fase di compilazione, puoi causare un errore e far sì che il compilatore lo raccolga.

Ad esempio, imposta la variabile su un tipo che non funziona ( let () = x; potrebbe funzionare anche):

 error[E0308]: mismatched types --> :2:29 | 2 | let mut my_number: () = 32.90; | ^^^^^ expected (), found floating-point variable | = note: expected type `()` = note: found type `{float}` error: aborting due to previous error 

O nella maggior parte dei casi, chiama un metodo non valido o ottieni un campo non valido :

 error: no method named `what_is_this` found for type `{float}` in the current scope --> :3:15 | 3 | my_number.what_is_this(); | ^^^^^^^^^^^^ error: aborting due to previous error 
 error: attempted access of field `what_is_this` on type `{float}`, but no field with that name was found --> :3:5 | 3 | my_number.what_is_this | ^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error 

Questi rivelano il tipo, che in questo caso in realtà non è completamente risolto. Si chiama “variabile floating-point” nel primo esempio e ” {float} ” in tutti e tre gli esempi; questo è un tipo parzialmente risolto che potrebbe finire in f32 o f64 , a seconda di come lo si usa. ” {float} ” non è un nome di tipo legale, è un segnaposto che significa “Non sono completamente sicuro di cosa sia”, ma è un numero a virgola mobile. Nel caso di variabili a virgola mobile, se non lo vincolate, verrà f64 su f64 ¹. (Un valore intero non specificato sarà predefinito in i32 .)


¹ Potrebbero esserci ancora modi per sconcertare il compilatore in modo che non possa decidere tra f32 e f64 ; Non ne sono sicuro. Era semplice come 32.90.eq(&32.90) , ma tratta sia come f64 ora sia come f64 lungo con gioia, quindi non lo so.

Esiste una funzione instabile std::intrinsics::type_name che può ottenere il nome di un tipo, anche se è necessario utilizzare una build notturna di Rust (è improbabile che funzioni mai in Rust stabile). Ecco un esempio:

 #![feature(core_intrinsics)] fn print_type_of(_: &T) { println!("{}", unsafe { std::intrinsics::type_name::() }); } fn main() { print_type_of(&32.90); // prints "f64" print_type_of(&vec![1, 2, 4]); // prints "std::vec::Vec" print_type_of(&"foo"); // prints "&str" } 

Se conosci tutti i tipi in anticipo, puoi utilizzare i tratti per aggiungere un metodo 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" } } //... 

No intrisics o nothin ‘, quindi anche se più limitato questa è l’unica soluzione qui che ti dà una stringa ed è stabile. Tuttavia, è molto laborioso e non tiene conto dei parametri di tipo, quindi potremmo …

 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() } } macro_rules! type_of { ($x:expr) => { (&$x).type_of() }; } 

Usiamolo:

 impl_type_info!(i32, i64, f32, f64, str, String, Vec, Result) fn main() { println!("{}", type_of!(1)); println!("{}", type_of!(&1)); println!("{}", type_of!(&&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!("{}", >::type_name()); println!("{}", <&i32>::type_name()); println!("{}", <&str>::type_name()); } 

produzione:

 i32 &i32 &&i32 f64 &str &&str String Vec Result &i32 &str 

Rust Playground

UPD Quanto segue non funziona più. Controlla la risposta di Shubham per la correzione.

Controlla std::intrinsics::get_tydesc() . È in stato “sperimentale” in questo momento, ma è OK se si sta solo hackerando il sistema dei tipi.

Guarda il seguente esempio:

 fn print_type_of(_: &T) -> () { let type_name = unsafe { (*std::intrinsics::get_tydesc::()).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" } 

Questo è ciò che viene utilizzato internamente per implementare il famoso formattatore {:?} .

Ho messo insieme una piccola cassa per fare ciò basandomi sulla risposta di vbo. Ti dà una macro per restituire o stampare il tipo.

Metti questo nel tuo file Cargo.toml:

 [dependencies] t_bang = "0.1.2" 

Quindi puoi usarlo in questo modo:

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

Puoi anche usare l’approccio semplice di usare la variabile in println!("{:?}", var) . Se Debug non è implementato per il tipo, puoi vedere il tipo nel messaggio di errore del compilatore:

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

( box )

È sporco ma funziona.