ps4 - rust steam
¿Cómo imprimo el tipo de una variable en Rust? (6)
Arreglé una pequeña caja para hacer esto en función de la respuesta de vbo. Le da una macro para devolver o imprimir el tipo.
Pon esto en tu archivo Cargo.toml:
[dependencies]
t_bang = "0.1.2"
Entonces puedes usarlo así:
#[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"
}
Tengo este fragmento de código:
let mut my_number = 32.90;
Necesito saber el tipo de my_number
. Usar type
y type_of
no funcionó; ¿Hay alguna otra forma en que pueda imprimir el tipo de número?
Existe una función inestable std::intrinsics::type_name
que puede darle el nombre de un tipo, aunque debe usar una compilación nocturna de Rust (es poco probable que esto funcione alguna vez en Rust estable). Aquí hay un ejemplo:
#![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"
}
Si conoce todos los tipos de antemano, puede usar rasgos para agregar un método 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"
}
}
//...
Sin intrínsecos ni nada, así que aunque es más limitada, esta es la única solución que te ofrece una cadena y es estable. Sin embargo, es muy laborioso y no tiene en cuenta los parámetros de tipo, por lo que podríamos ...
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() };
}
Vamos a usarlo:
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!(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());
}
salida:
i32
&i32
&&i32
f64
&str
&&str
String
Vec<i32>
Result<String,i64>
&i32
&str
Si simplemente desea averiguar el tipo de una variable y está dispuesto a hacerlo en tiempo de compilación, puede provocar un error y hacer que el compilador la recoja.
Por ejemplo, establezca la variable en un tipo que no funcione ( let () = x;
también funcionaría):
error[E0308]: mismatched types
--> <anon>: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 en la mayoría de los casos, llame a un método no válido u obtenga un campo no válido :
error: no method named `what_is_this` found for type `{float}` in the current scope
--> <anon>: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
--> <anon>:3:5
|
3 | my_number.what_is_this
| ^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to previous error
Estos revelan el tipo, que en este caso no está completamente resuelto. Se llama "variable de punto flotante" en el primer ejemplo, y " {float}
" en los tres ejemplos; este es un tipo parcialmente resuelto que podría terminar en f32
o f32
, dependiendo de cómo lo use. " {float}
" no es un nombre de tipo legal, es un marcador de posición que significa "No estoy completamente seguro de qué es esto", pero es un número de coma flotante. En el caso de variables de punto flotante, si no lo restringe, se usará de manera predeterminada a f64
¹. (Un literal entero no calificado se i32
en i32
por defecto).
¹ Todavía puede haber formas de desconcertar al compilador para que no pueda decidir entre f32
y f32
; No estoy seguro. Solía ser tan simple como 32.90.eq(&32.90)
, pero eso trata a ambos como f64
ahora y resuena alegremente, así que no lo sé.
También puede usar el enfoque simple de usar la variable en println!("{:?}", var)
. Si Debug
no está implementado para el tipo, puede ver el tipo en el mensaje de error del compilador:
mod some {
pub struct SomeType;
}
fn main() {
let unknown_var = some::SomeType;
println!("{:?}", unknown_var);
}
( playpen )
Está sucio pero funciona.
UPD Lo siguiente ya no funciona. Verifique la respuesta de Shubham para la corrección.
Consulte std::intrinsics::get_tydesc<T>()
. Está en estado "experimental" en este momento, pero está bien si solo estás pirateando el sistema de tipos.
Mira el siguiente ejemplo:
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>"
}
Esto es lo que se usa internamente para implementar el famoso formateador {:?}
.