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.