Kann ich Eigeninterpretation mit Merkmalsobjekten schreiben und sie dann ablehnen?

Ich habe eine Sammlung von Trait , eine function, die darüber iteriert und etwas tut, und dann möchte ich den Implementor-Typ überprüfen, und wenn es vom Typ Foo dann stürze es ab und rufe eine Foo-Methode auf.

Im Grunde genommen etwas Ähnliches wie Go’s Typ-Switch und Interface-Konvertierung .

In der Umgebung habe ich die Any-Eigenschaft gefunden, aber sie kann nur auf 'static Typen implementiert werden.

Um zu demonstrieren, was ich will:

 let vec: Vec<Box> = // for e in vec.iter() { e.trait_method(); // if typeof e == Foo { // let f = e as Foo; // f.foo_method(); //} } 

   

Wie Sie bemerkt haben, funktioniert Downcasting nur mit Any Eigenschaft, und ja, es unterstützt nur 'static Daten. Sie können eine aktuelle Diskussion darüber finden, warum es so ist . Grundsätzlich ist die Implementierung von Reflektion für Referenzen von beliebigen Lebensdauern schwierig.

Es ist auch nicht möglich (zumindest jetzt) Any mit Ihrem eigenen Merkmal zu kombinieren. Es wurde jedoch kürzlich eine Makrobibliothek für die automatische Implementierung von Any für Ihr Merkmal erstellt. Sie können hier auch eine Diskussion darüber finden .

Dies ist kein Rust-spezifisches Problem, obwohl das Vokabular ein wenig anders sein kann. Der ideale Weg, um ein Problem wie dieses nicht nur mit Rust-Merkmalen, sondern in jeder Sprache zu lösen, besteht darin, das gewünschte Verhalten ( foo_method in Ihrem Beispiel) der abstrakten Schnittstelle ( foo_method ) hinzuzufügen:

 trait Trait { fn trait_method(&self); fn foo_method(&self) {} // does nothing by default } struct Foo; impl Trait for Foo { fn trait_method(&self) { println!("In trait_method of Foo"); } fn foo_method(&self) { // override default behavior println!("In foo_method"); } } struct Bar; impl Trait for Bar { fn trait_method(&self) { println!("In trait_method of Bar"); } } fn main() { let vec: Vec> = vec![Box::new(Foo), Box::new(Bar)]; for e in &vec { e.trait_method(); e.foo_method(); } } 

In diesem Beispiel habe ich eine Standardimplementierung von foo_method in Trait foo_method die nichts tut, so dass Sie sie nicht in jedem impl definieren impl sondern nur in dem, in dem sie impl . Sie sollten wirklich versuchen, die obige Arbeit zu machen, bevor Sie auf einen konkreten Typ zurückgehen, der schwerwiegende Nachteile hat, die die Vorzüge von Merkmalsobjekten überhaupt zunichte machen.

Das heißt, es gibt Fälle, in denen downcasting notwendig sein könnte, und Rust unterstützt es – obwohl die Schnittstelle ein wenig klobig ist. Du kannst Downcast &Trait to &Foo hinzufügen, indem du eine Zwischenabstufung zu &Any hinzufügst:

 use std::any::Any; trait Trait { fn as_any(&self) -> &Any; } struct Foo; impl Trait for Foo { fn as_any(&self) -> &Any { self } } fn downcast(this: &Trait) -> Option< &T> { this.as_any().downcast_ref() } 

as_any muss in Trait eine Methode sein, da sie Zugriff auf den konkreten Typ benötigt. Jetzt können Sie versuchen, Foo Methoden für ein Trait Merkmalsobjekt wie dieses aufzurufen ( vollständiges Beispiel für einen Spielplatz ):

 if let Some(r) = downcast::(trait_object_ref) { r.foo_method(); } 

Um dies zu erreichen, müssen Sie angeben, welchen Typ Sie erwarten ( :: ) und verwenden, if let behandeln if let , was passiert, wenn das referenzierte Objekt keine Instanz von Foo . Sie können ein Eigenschaftenobjekt nicht auf einen konkreten Typ reduzieren, wenn Sie nicht genau wissen, um welchen konkreten Typ es sich handelt.

Aber wenn du jemals den konkreten Typ kennen musst, sind Merkmalsobjekte sowieso fast nutzlos! Sie sollten wahrscheinlich stattdessen eine enum verwenden, so dass Sie Fehler bei der Kompilierung bekommen, wenn Sie nicht irgendwo mit einer Variante umgehen. Außerdem können Sie Any mit nicht 'static Strukturen nicht verwenden. Wenn also ein Foo eine Referenz enthalten muss, ist dieses Design eine Sackgasse. Die beste Lösung, wenn Sie es tun können, ist foo_method zu dem Merkmal selbst hinzuzufügen.