490 Stimmen

Wie drucke ich in Rust den Typ einer Variablen?

Ich habe Folgendes:

let mut my_number = 32.90;

Wie drucke ich den Typ von my_number aus?

Die Verwendung von type und type_of hat nicht funktioniert. Gibt es einen anderen Weg, um den Typ der Zahl auszudrucken?

471voto

Boiethios Punkte 30647

Sie können die std::any::type_name Funktion verwenden. Hierfür ist kein Nightly-Compiler oder externe Crate erforderlich, und die Ergebnisse sind ziemlich korrekt:

fn print_type_of(_: &T) {
    println!("{}", std::any::type_name::())
}

fn main() {
    let s = "Hallo";
    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::); // playground::print_type_of
    print_type_of(&{ || "Hallo!" }); // playground::main::{{closure}}
}

Seien Sie gewarnt: Wie in der Dokumentation erwähnt, sollte diese Information nur für Debugging-Zwecke verwendet werden:

Dies ist für diagnostische Zwecke gedacht. Der genaue Inhalt und das Format des Strings sind nicht spezifiziert, außer dass es eine bestmögliche Beschreibung des Typs ist.

Wenn Sie möchten, dass Ihre Typ-Repräsentation zwischen Compiler-Versionen gleich bleibt, sollten Sie ein Trait verwenden, ähnlich wie in der Antwort von phicr.

287voto

Chris Morgan Punkte 78929

Wenn Sie lediglich den Typ einer Variablen ermitteln möchten und bereit sind, dies zur Kompilierzeit zu tun, können Sie einen Fehler verursachen und den Compiler dazu bringen, dies aufzuheben.

Zum Beispiel, [setzen Sie die Variable auf einen Typ, der nicht funktioniert](https://play.rust-lang.org/?code=fn%20main()%20%7B%0A%20%20%20%20let%20mut%20my_number%3A%20()%20%3D%2032.90%3B%0A%7D&version=stable&backtrace=2&run=1):

let mut my_number: () = 32.90;
// let () = x; würde auch funktionieren

Fehler[E0308]: unpassende Typen
 --> src/main.rs:2:29
  |
2 |     let mut my_number: () = 32.90;
  |                             ^^^^^ erwartet (), gefunden Gleitkommazahl
  |
  = Hinweis: Erwarteter Typ `()`
             Gefundener Typ `{float}`

Oder [rufen Sie eine ungültige Methode auf](https://play.rust-lang.org/?code=fn%20main()%20%7B%0A%20%20%20%20let%20mut%20my_number%20%3D%2032.90%3B%0A%20%20%20%20my_number.what_is_this()%3B%0A%7D&version=stable&backtrace=2&run=1):

let mut my_number = 32.90;
my_number.what_is_this();

Fehler[E0599]: keine Methode mit dem Namen `what_is_this` für den Typ `{float}` im aktuellen Gültigkeitsbereich gefunden
 --> src/main.rs:3:15
  |
3 |     my_number.what_is_this();
  |               ^^^^^^^^^^^^

Oder [zugreifen auf ein ungültiges Feld](https://play.rust-lang.org/?code=fn%20main()%20%7B%0A%20%20%20%20let%20mut%20my_number%20%3D%2032.90%3B%0A%20%20%20%20my_number.what_is_this%0A%7D&version=stable&backtrace=2&run=1):

let mut my_number = 32.90;
my_number.what_is_this

Fehler[E0610]: `{float}` ist ein primitiver Typ und hat daher keine Felder
 --> src/main.rs:3:15
  |
3 |     my_number.what_is_this
  |               ^^^^^^^^^^^^

Diese offenbaren den Typ, der in diesem Fall tatsächlich nicht vollständig aufgelöst ist. Es wird im ersten Beispiel als "Gleitkommavariable" bezeichnet und in allen drei Beispielen als "{float}"; dies ist ein teilweise aufgelöster Typ, der letztendlich zu f32 oder f64 führen könnte, je nach Verwendung. "{float}" ist kein gültiger Typname, sondern ein Platzhalter, der bedeutet "Ich bin mir nicht ganz sicher, was das ist", aber es handelt sich tatsächlich um eine Gleitkommazahl. Im Fall von Gleitkommavariablen wird der Typ default auf f64¹ festgelegt, wenn Sie ihn nicht einschränken. (Ein nicht qualifizierter Ganzzahlliterale wird standardmäßig zu i32.)

Siehe auch:


¹ Es gibt wahrscheinlich noch Möglichkeiten, den Compiler zu verwirren, sodass er nicht zwischen f32 und f64 entscheiden kann; Ich bin mir nicht sicher. Es war früher so einfach wie 32.90.eq(&32.90), aber das behandelt beide jetzt als f64 und funktioniert ohne Probleme, also weiß ich nicht.

145voto

Shubham Jain Punkte 1429

Es gibt eine instabile Funktion std::intrinsics::type_name, die den Namen eines Typs zurückgeben kann, obwohl Sie eine nächtliche Version von Rust verwenden müssen (dies wird wahrscheinlich niemals in der stabilen Version von Rust funktionieren). Hier ist ein Beispiel:%3B%0A%7D%0A%0Afn%20main()%20%7B%0A%20%20%20%20print_type_of(%2632.90)%3B%20%20%20%20%20%20%20%20%20%20%2F%2F%20prints%20%22f64%22%0A%20%20%20%20print_type_of(%26vec!%5B1%2C%202%2C%204%5D)%3B%20%20%2F%2F%20prints%20%22std%3A%3Avec%3A%3AVec%3Ci32%3E%22%0A%20%20%20%20print_type_of(%26%22foo%22)%3B%20%20%20%20%20%20%20%20%20%20%2F%2F%20prints%20%22%26str%22%0A%7D&version=nightly&backtrace=2&run=1)

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

85voto

phicr Punkte 1232

Wenn Sie alle Typen im Voraus kennen, können Sie Traits verwenden, um eine type_of-Methode hinzuzufügen:

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

//...

Keine Intrinsika oder sonst etwas, daher obwohl etwas begrenzter ist dies die einzige Lösung hier, die Ihnen einen String liefert und stabil ist. (siehe Boiethios's Antwort) Es ist jedoch sehr mühsam und berücksichtigt keine Typparameter, daher könnten wir...

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

Lassen Sie uns es verwenden:

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!(&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!("{}", >::type_name());
    println!("{}", <&i32>::type_name());
    println!("{}", <&str>::type_name());
}

Ausgabe:

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

Rust Playground

30voto

Black Marco Punkte 581

Aktualisierung, ursprüngliche Antwort unten

Wie wäre es mit der Trait-Funktion type_name, die nützlich ist, um den Typnamen schnell zu erhalten.

pub trait AnyExt {
    fn type_name(&self) -> &'static str;
}

impl AnyExt for T {
    fn type_name(&self) -> &'static str {
        std::any::type_name::()
    }
}

fn main(){
    let my_number = 32.90;
    println!("{}",my_number.type_name());
}

Ausgabe:

f64

Ursprüngliche Antwort

Ich schreibe ein Makro type_of!() zum Debuggen, das ursprünglich von std dbg!() stammt.

pub fn type_of2(v: T) -> (&'static str, T) {
    (std::any::type_name::(), v)
}

#[macro_export]
macro_rules! type_of {
    // HINWEIS: Wir können `concat!` nicht verwenden, um einen statischen String als Formatargument von `eprintln!` zu erzeugen, da `file!` eine `{` enthalten könnte oder `$val` ein Block (`{ .. }`) sein könnte, in welchem Fall das `eprintln!` fehlerhaft wäre.
    () => {
        eprintln!("[{}:{}]", file!(), line!());
    };
    ($val:expr $(,)?) => {
        // Die Verwendung von `match` hier ist beabsichtigt, weil es sich auf die Lebensdauern von temporären Variablen auswirkt - https://stackoverflow.com/a/48732525/1063961
        match $val {
            tmp => {
                let (type_,tmp) = $crate::type_of2(tmp);
                eprintln!("[{}:{}] {}: {}",
                    file!(), line!(), stringify!($val), type_);
                tmp
            }
        }
    };
    ($($val:expr),+ $(,)?) => {
        ($($crate::type_of!($val)),+,)
    };
}

fn main(){
    let my_number = type_of!(32.90);
    type_of!(my_number);
}

Ausgabe:

[src/main.rs:32] 32.90: f64
[src/main.rs:33] my_number: f64

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X