Zuallererst hat Rust keine Reflexion; Reflexion bedeutet, dass Sie Details über einen Typ zur Laufzeit erhalten können, wie die Felder, Methoden, Interfaces, die er implementiert, etc. Sie kann nicht tun dies mit Rust. Der nächste, den Sie erhalten können, ist das explizite Implementieren (oder Ableiten) eines Merkmals, das diese Information bereitstellt.
Jedem Typ wird beim Kompilieren ein TypeId
zugewiesen. Da global geordnete IDs hart sind, ist die ID eine Ganzzahl, die aus einer Kombination der Typdefinition und assozierten Metadaten über die Kiste, in der sie enthalten ist, abgeleitet wird. Um es anders auszudrücken: Sie sind nicht in irgendeiner Reihenfolge angeordnet, sie sind nur Hashes der verschiedenen Bits von Informationen, die in die Definition des Typs eingehen. [1]
Wenn man sich die source for the Any
trait anschauen, werden Sie die einzige Implementierung für Any
sehen:
impl<T: Reflect + 'static + ?Sized> Any for T {
fn get_type_id(&self) -> TypeId { TypeId::of::<T>() }
}
(Die Grenzen informell reduziert werden kann „alle Arten, die von nicht ausgeliehen werden etwas anderes“)
Sie auch die Definition von TypeId
finden.
pub struct TypeId {
t: u64,
}
impl TypeId {
pub fn of<T: ?Sized + Reflect + 'static>() -> TypeId {
TypeId {
t: unsafe { intrinsics::type_id::<T>() },
}
}
}
intrinsics::type_id
ist eine vom Compiler erkannte interne Funktion, die bei einem Typ die interne Typ-ID zurückgibt. Dieser Aufruf wird nur zur Kompilierzeit durch die literale Integer-Typ-ID ersetzt; Es gibt keine tatsächlichen hier anrufen. [2] So weiß TypeId
, was die ID eines Typs ist. TypeId
, dann ist nur ein Wrapper um diese u64
, um die Implementierungsdetails von Benutzern zu verbergen. Wenn Sie es konzeptionell einfacher finden, können Sie sich die TypeId
eines Typs als eine konstante 64-Bit-Ganzzahl vorstellen, die der Compiler zum Kompilieren nur kennt.
Any
vorwärts dies von get_type_id
, was bedeutet, dass get_type_id
ist wirklich das Merkmal Methode zum entsprechenden TypeId::of
Verfahren nur verbindlich. Es ist nur da, um sicherzustellen, dass, wenn Sie eine Any
haben, können Sie die ursprüngliche Art TypeId
herausfinden.
Nun wird Any
für meist Typen implementiert, aber das bedeutet nicht, dass alle Arten tatsächlich eine Any
Implementierung Umlauf im Speicher.Was tatsächlich passiert, ist, dass der Compiler nur den tatsächlichen Code für die Implementierung Any
eines Typs generiert, wenn jemand Code schreibt, der es erfordert. [3] Mit anderen Worten, wenn Sie niemals die Any
Implementierung für einen bestimmten Typ verwenden, wird der Compiler sie niemals erzeugen.
So erfüllt Rust "nicht bezahlen für das, was Sie nicht verwenden": Wenn Sie einen gegebenen Typ nie als &Any
oder Box<Any>
übergeben, wird der zugehörige Code nie generiert und nimmt niemals Platz in Ihrer kompilierten Binärdatei ein .
[1]: Frustrierend, bedeutet dies, dass ein auf TypeId
kann der Typ Wertänderung je genau wie die Bibliothek kompiliert wird, bis zu dem Punkt, dass es als Abhängigkeit Kompilieren (wie ein Gegensatz zu Standalone Build) bewirkt, dass sich TypeId
s ändert.
[2]: Soweit ich weiß. Ich könnte falsch darüber sein, aber ich wäre wirklich überrascht, wenn das der Fall ist.
[3]: Dies ist allgemein wahren von Generika in Rust.
Interessanterweise verwendet [die Dokumentation für std :: any] (https://doc.rust-lang.org/std/any/) den Ausdruck "Laufzeitreflexion", obwohl es wahrscheinlich ist, dass sie sich darauf beziehen Das ist nicht das, was die meisten von uns mit "Laufzeitreflexion" meinen, da dies nur Typprüfung und Typcasting ermöglicht, anstatt den Inhalt einer beliebigen Struktur zu untersuchen. – Ixrec
OK. Mit Reflexion meine ich RTTI. –
Sie können "TypeId" ohne "Any" für begrenzte Formen von Ad-hoc-Sondertypen pro Typ ohne Laufzeitkosten verwenden. – bluss