Ich habe folgendes:
let mut my_number = 32.90;
Wie drucke ich den Typ von my_number
?
Verwenden type
und type_of
hat nicht funktioniert. Gibt es eine andere Möglichkeit, den Typ der Nummer auszudrucken?
Ich habe folgendes:
let mut my_number = 32.90;
Wie drucke ich den Typ von my_number
?
Verwenden type
und type_of
hat nicht funktioniert. Gibt es eine andere Möglichkeit, den Typ der Nummer auszudrucken?
Antworten:
Wenn Sie lediglich den Typ einer Variablen herausfinden möchten und dies zur Kompilierungszeit tun möchten , können Sie einen Fehler verursachen und den Compiler veranlassen, ihn abzurufen.
Zum Beispiel setzen Sie die Variable auf eine Art , die nicht funktioniert :
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}`
Oder rufen Sie eine ungültige Methode auf :
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();
| ^^^^^^^^^^^^
Oder greifen Sie auf ein ungültiges Feld zu :
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
| ^^^^^^^^^^^^
Diese zeigen den Typ, der in diesem Fall tatsächlich nicht vollständig aufgelöst ist. Im ersten Beispiel heißt es "Gleitkommavariable" und {float}
in allen drei Beispielen " ". Dies ist ein teilweise aufgelöster Typ, der enden kann f32
oder f64
je nachdem, wie Sie ihn verwenden. " {float}
" Ist kein legaler Typname, sondern ein Platzhalter, der "Ich bin mir nicht ganz sicher, was das ist" bedeutet, aber es ist eine Gleitkommazahl. Wenn Sie bei Gleitkommavariablen diese nicht einschränken, wird standardmäßig f64
¹ verwendet. (Ein unqualifiziertes Integer-Literal wird standardmäßig verwendet i32
.)
Siehe auch:
¹ Möglicherweise gibt es immer noch Möglichkeiten, den Compiler zu verwirren, sodass er sich nicht zwischen f32
und entscheiden kann f64
. Ich bin mir nicht sicher. Früher war es so einfach wie jetzt 32.90.eq(&32.90)
, aber das behandelt sowohl f64
jetzt als auch tuckert glücklich mit, also weiß ich es nicht.
ImageBuffer<_, Vec<_>>
nicht sehr hilft, wenn ich versuche, eine Funktion zu schreiben, die eines dieser Dinge als Parameter verwendet. Und dies geschieht in Code, der ansonsten kompiliert wird, bis ich den hinzufüge :()
. Gibt es keinen besseren Weg?
Es gibt eine instabile Funktion std::intrinsics::type_name
, mit der Sie den Namen eines Typs erhalten können, obwohl Sie einen nächtlichen Build von Rust verwenden müssen (dies wird wahrscheinlich nie in stabilem Rust funktionieren). Hier ist ein Beispiel:
#![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"
}
#![feature(core_intrinsics)]
print_type_of
nimmt Referenzen ( &T
), nicht Werte ( T
), also müssen Sie übergeben &&str
anstatt &str
; das heißt, print_type_of(&"foo")
eher als print_type_of("foo")
.
std::any::type_name
ist stabil seit Rost 1.38: stackoverflow.com/a/58119924
Sie können die std::any::type_name
Funktion verwenden. Dies erfordert keinen nächtlichen Compiler oder eine externe Kiste, und die Ergebnisse sind ganz richtig:
fn print_type_of<T>(_: &T) {
println!("{}", std::any::type_name::<T>())
}
fn main() {
let s = "Hello";
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::<i32>); // playground::print_type_of<i32>
print_type_of(&{ || "Hi!" }); // playground::main::{{closure}}
}
Seien Sie gewarnt: Wie in der Dokumentation angegeben, dürfen diese Informationen nur für Debug-Zwecke verwendet werden:
Dies ist für diagnostische Zwecke vorgesehen. Der genaue Inhalt und das Format der Zeichenfolge werden nicht angegeben, außer dass es sich um eine Best-Effort-Beschreibung des Typs handelt.
Wenn Sie möchten, dass Ihre Typdarstellung zwischen den Compilerversionen gleich bleibt, sollten Sie ein Merkmal verwenden, wie in der Antwort des Phicr .
Wenn Sie alle Typen im Voraus kennen, können Sie mithilfe von Merkmalen eine type_of
Methode hinzufü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 Feinheiten oder nichts, obwohl dies eingeschränkter ist, ist dies die einzige Lösung hier, die Ihnen einen String gibt und stabil ist. (Siehe die Antwort von French Boiethios. ) Es ist jedoch sehr mühsam und berücksichtigt keine Typparameter, sodass 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() };
}
Lass es uns benutzen:
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());
}
Ausgabe:
i32
&i32
&&i32
&mut i32
&&mut i32
&mut &i32
f64
&str
&&str
String
Vec<i32>
Result<String,i64>
&i32
&str
UPD Folgendes funktioniert nicht mehr. Überprüfen Sie Shubhams Antwort auf Korrektur.
Auschecken std::intrinsics::get_tydesc<T>()
. Es befindet sich derzeit im "experimentellen" Zustand, aber es ist in Ordnung, wenn Sie nur das Typsystem hacken.
Schauen Sie sich das folgende Beispiel an:
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>"
}
Dies wird intern verwendet , um den berühmten {:?}
Formatierer zu implementieren .
** UPDATE ** Es wurde in letzter Zeit nicht überprüft, ob dies funktioniert.
Ich habe eine kleine Kiste zusammengestellt, um dies basierend auf der Antwort von vbo zu tun. Sie erhalten ein Makro, mit dem Sie den Typ zurückgeben oder ausdrucken können.
Fügen Sie dies in Ihre Cargo.toml-Datei ein:
[dependencies]
t_bang = "0.1.2"
Dann können Sie es so verwenden:
#[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"
}
#![feature]
darf nicht auf dem stabilen Release-Kanal verwendet werden`
Sie können auch den einfachen Ansatz verwenden, die Variable in zu verwenden println!("{:?}", var)
. Wenn Debug
für den Typ nicht implementiert, wird der Typ in der Fehlermeldung des Compilers angezeigt:
mod some {
pub struct SomeType;
}
fn main() {
let unknown_var = some::SomeType;
println!("{:?}", unknown_var);
}
( Laufstall )
Es ist schmutzig, aber es funktioniert.
Debug
nicht implementiert ist, ist dies jedoch ein ziemlich unwahrscheinlicher Fall. Eines der ersten Dinge, die Sie für die meisten Strukturen tun sollten, ist das Hinzufügen #[derive(Debug)]
. Ich denke, die Zeiten, in denen du nicht willst, Debug
sind sehr klein.
println!("{:?}", unknown_var);
? Ist es eine String-Interpolation, aber warum das :?
Innere der geschweiften Klammern? @ DenisKolodin
Debug
weil es nicht implementiert ist, aber Sie können es auch verwenden {}
.
Es gibt eine @ ChrisMorgan- Antwort , um den ungefähren Typ ("float") in stabilem Rost zu erhalten, und es gibt eine @ ShubhamJain- Antwort , um einen genauen Typ ("f64") durch instabile Funktion in nächtlichem Rost zu erhalten.
Hier ist eine Möglichkeit, wie man einen präzisen Typ (dh zwischen f32 und f64 entscheiden) für stabilen Rost erhalten kann:
fn main() {
let a = 5.;
let _: () = unsafe { std::mem::transmute(a) };
}
führt zu
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)
Aktualisieren
Die Turbofisch-Variante
fn main() {
let a = 5.;
unsafe { std::mem::transmute::<_, ()>(a) }
}
ist etwas kürzer aber etwas weniger lesbar.
float
, zu sagen zwischen f32
und f64
erreicht werden kann , mitstd::mem::size_of_val(&a)
Einige andere Antworten funktionieren nicht, aber ich finde, dass die Typennamen- Kiste funktioniert.
Erstellen Sie ein neues Projekt:
cargo new test_typename
Ändern Sie die Cargo.toml
[dependencies]
typename = "0.1.1"
Ändern Sie Ihren Quellcode
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());
}
Die Ausgabe ist:
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
typename
nicht mehr mit Variablen ohne expliziten Typ in der Deklaration. Wenn Sie es mit my_number
der Frage ausführen, wird der folgende Fehler type_name_of
{float}
f32
0.65
und es funktioniert gut : type of c 0.65 0.65 is f64
. Hier ist meine Version:rustc 1.38.0-nightly (69656fa4c 2019-07-13)
Wenn Sie nur den Typ Ihrer Variablen während der interaktiven Entwicklung kennen möchten, würde ich die Verwendung von rls ( Rust Language Server) in Ihrem Editor oder Ihrer Idee sehr empfehlen . Sie können dann einfach die Schwebefähigkeit dauerhaft aktivieren oder umschalten und den Cursor einfach über die Variable bewegen. In einem kleinen Dialogfeld sollten Informationen zur Variablen einschließlich des Typs angezeigt werden.
:?
wurde schon lange manuell implementiert. Noch wichtiger ist jedoch, dass diestd::fmt::Debug
Implementierung (für die dies:?
verwendet wird) für Zahlentypen kein Suffix mehr enthält, das angibt, um welchen Typ es sich handelt.