web-dev-qa-db-fra.com

Comment imprimer le type d'une variable dans Rust?

J'ai le suivant:

let mut my_number = 32.90;

Comment imprimer le type de my_number?

L'utilisation de type et type_of ne fonctionnait pas. Existe-t-il un autre moyen d'imprimer le type du numéro?

189
user2431012

Si vous souhaitez simplement connaître le type d'une variable et êtes prêt à le faire au moment de la compilation, vous pouvez provoquer une erreur et faire en sorte que le compilateur ramasser.

Par exemple, définissez la variable sur un type qui ne fonctionne pas :

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}`

Ou appelez une méthode invalide :

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();
  |               ^^^^^^^^^^^^

Ou accéder à un champ invalide :

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
  |               ^^^^^^^^^^^^

Celles-ci révèlent le type, qui dans ce cas n'est pas complètement résolu. Elle s’appelle "variable à virgule flottante" dans le premier exemple et "{float}" dans les trois exemples; Il s'agit d'un type partiellement résolu pouvant aboutir à f32 ou f64, selon l'utilisation que vous en faites. “{float}” n'est pas un nom de type légal, c'est un espace réservé signifiant “je ne suis pas tout à fait sûr de quoi il s'agit”, mais il est un nombre à virgule flottante. Dans le cas de variables à virgule flottante, si vous ne la contraignez pas, la valeur par défaut sera f64¹. (Un littéral entier non qualifié sera par défaut à i32.)

Voir également:


¹ Il est encore possible de dérouter le compilateur pour qu’il ne puisse pas décider entre f32 et f64; Je ne suis pas sûr. C’était jadis aussi simple que 32.90.eq(&32.90), mais cela traite à la fois f64 et avance joyeusement, alors je ne le sais pas.

148
Chris Morgan

Il existe une fonction instable std::intrinsics::type_name qui peut vous donner le nom d'un type, bien que vous deviez utiliser une version nocturne de Rust (il est peu probable que cela fonctionne jamais dans la rouille stable). Voici un exemple:

_#![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"
}
_
89
Shubham Jain

Si vous connaissez tous les types à l'avance, vous pouvez utiliser des traits pour ajouter une méthode 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"
    }
}

//...

Aucune intrisics ou rien, donc bien que plus limité c'est la seule solution ici qui vous obtient une chaîne et est stable. Cependant, c'est très laborieux et ne tient pas compte des paramètres de type, alors nous pourrions ...

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() };
}

Utilisons-le:

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());
}

sortie:

i32
&i32
&&i32
&mut i32
&&mut i32
&mut &i32
f64
&str
&&str
String
Vec<i32>
Result<String,i64>
&i32
&str

Terrain de jeu de la rouille

47
phicr

UPD Ce qui suit ne fonctionne plus. Vérifiez réponse de Shubham pour correction.

Découvrez std::intrinsics::get_tydesc<T>(). Il est actuellement dans un état "expérimental", mais cela ne pose pas de problème si vous vous contentez de pirater le système de types.

Découvrez l'exemple suivant:

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

C’est ce qui est tilisé en interne pour implémenter le célèbre formateur {:?}.

18
vbo

J'ai mis en place une petite caisse pour le faire basée sur la réponse de vbo. Il vous donne une macro pour retourner ou imprimer le type.

Mettez ceci dans votre fichier Cargo.toml:

[dependencies]
t_bang = "0.1.2"

Ensuite, vous pouvez l'utiliser comme ceci:

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

Vous pouvez également utiliser l'approche simple consistant à utiliser la variable dans println!("{:?}", var). Si Debug n'est pas implémenté pour le type, vous pouvez voir le type dans le message d'erreur du compilateur:

mod some {
    pub struct SomeType;
}

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

( parc )

C'est sale mais ça marche.

6
DenisKolodin

Il y a un @ChrisMorgan réponse pour obtenir un type approximatif ("float") dans stable Rust et il y a un @ShubhamJain réponse pour obtenir un type précis ("f64 ") à travers la fonction instable dans la nuit de rouille.

Voici un moyen d'obtenir un type précis (c.-à-d. Choisir entre f32 et f64) dans Rust stable:

fn main() {
    let a = 5.;
    let _: () = unsafe { std::mem::transmute(a) };
}

résulte en

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)

Mettre à jour

La variation de turbofish

fn main() {
    let a = 5.;
    unsafe { std::mem::transmute::<_, ()>(a) }
}

est légèrement plus court mais un peu moins lisible.

2
Antony Hatchkins

Certaines autres réponses ne fonctionnent pas, mais je trouve que la caisse typename fonctionne.

  1. Créer un nouveau projet:

    cargo new test_typename
    
  2. Modifier le Cargo.toml

    [dependencies]
    typename = "0.1.1"
    
  3. Modifier votre code source

    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());
    }
    

La sortie est:

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
0
Flyq