2016-04-05 15 views
27

Rust hat die Any Eigenschaft, aber es hat auch eine Politik "nicht für das bezahlen, was Sie nicht verwenden". Wie implementiert Rust die Reflexion?Wie implementiert Rust Reflexion?

Meine Vermutung ist, dass Rust Faul Tagging verwendet. Jeder Typ wird anfänglich nicht zugewiesen, aber später, wenn eine Instanz des Typs an eine Funktion übergeben wird, die ein Any Merkmal erwartet, wird dem Typ ein TypeId zugewiesen.

Oder vielleicht Rust setzt einen TypeId auf jeden Typ, dass seine Instanz möglicherweise an diese Funktion übergeben wird? Ich denke, ersteres wäre teuer.

Antwort

37

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.

+1

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

+0

OK. Mit Reflexion meine ich RTTI. –

+0

Sie können "TypeId" ohne "Any" für begrenzte Formen von Ad-hoc-Sondertypen pro Typ ohne Laufzeitkosten verwenden. – bluss