2014-09-30 8 views
18

Ich habe eine Eigenschaft, die von einigen Strukturen implementiert wird. Ich möchte ein Mustervergleich schreiben, wo ich jeden denkbaren Fall umgehen kann:Wie Trait Implementoren

trait Base {} 

struct Foo { x: uint } 
struct Bar { y: uint } 

impl Base for Foo {} 
impl Base for Bar {} 

fn test(v: bool) -> Box<Base + 'static> { 
    // Let's pretend there's real logic that determines what to return. 
    if v { 
     box Foo { x: 5 } 
    } else { 
     box Bar { y: 10 } 
    } 
} 

fn main() { 
    let f: Box<Base> = test(true); 

    // Now that we have a `Box<Base>` (`*f` makes it a `Base`), 
    // let's handle different cases: 
    match *f { 
     Foo { x } => println!("it was Foo: {}!", x), 
     Bar { y } => println!("it was Bar: {}!", y), 
    } 
} 

(versuchen Sie es online: http://is.gd/YuBkPF)

Ich bekomme diese Kompilierungsfehler:

mismatched types: expected Base , found a structure pattern

Antwort

21

Sie können t. Eigenschaften unterstützen keine Downcasting - Rust ist keine Vererbung/Subtyping-basierte Sprache, und es gibt Ihnen eine weitere Reihe von Abstraktionen. Darüber hinaus ist das, was Sie tun wollen, nicht stichhaltig - Eigenschaften sind offen (jeder kann sie für irgendetwas implementieren), also selbst wenn in Ihrem Fall match *f alle möglichen Fälle abdeckt, kann der Compiler im Allgemeinen das nicht wissen.

Sie haben hier zwei Möglichkeiten. Wenn Sie die Menge der Strukturen kennen, die Ihr Merkmal im Voraus implementieren, benutzen Sie einfach enum, es ist ein perfektes Werkzeug dafür. Sie ermöglichen es, statisch auf einer geschlossenen Reihe von Varianten zum Spiel:

enum FooBar { 
    Foo(uint), 
    Bar(uint) 
} 

fn test(v: bool) -> FooBar { 
    if v { 
     Foo(5) 
    } else { 
     Bar(10) 
    } 
} 

fn main() { 
    let f: FooBar = test(true); 

    // Now that we have a `Box<Base>` (`*f` makes it a `Base`), 
    // let's handle different cases: 
    match f { 
     Foo(x) => println!("it was Foo: {}!", x), 
     Bar(y) => println!("it was Bar: {}!", y), 
    } 
} 

(versuchen Sie es here)

Dies ist bei weitem der einfachste Weg, und es sollte immer bevorzugt werden. Eine andere Möglichkeit ist es, Any Merkmal zu verwenden. Es ist eine Einrichtung für typsichere Downcasting von Zug Objekte regelmäßig Typen:

use std::any::{Any, AnyRefExt}; 

struct Foo { x: uint } 
struct Bar { y: uint } 

fn test(v: bool) -> Box<Any + 'static> { // ' 
    if v { 
     box Foo { x: 5 } 
    } else { 
     box Bar { y: 10 } 
    } 
} 

fn main() { 
    let f: Box<Any> = test(true); 

    match f.downcast_ref::<Foo>() { 
     Some(&Foo { x }) => println!("it was Foo: {}!", x), 
     None => match f.downcast_ref::<Bar>() { 
      Some(&Bar { y }) => println!("it was Bar: {}!", y), 
      None => unreachable!() 
     } 
    } 

// it will be nicer when `if let` lands 
// if let Some(&Foo { x }) = f.downcast_ref::<Foo>() { 
//  println!("it was Foo: {}!", x); 
// } else if let Some(&Bar { y }) = f.downcast_ref::<Bar>() { 
//  println!("it was Bar: {}!", y); 
// } else { unreachable!() } 
} 

(versuchen Sie es here)

Im Idealfall sollte es möglich sein, so etwas zu schreiben:

trait Base : Any {} 

impl Base for Foo {} 
impl Base for Bar {} 

und dann Base im Code verwenden, aber es kann jetzt nicht ausgeführt werden, da die Merkmalvererbung nicht mit Merkmalsobjekten funktioniert (z. B. ist es unmöglich, von Box<Base> zu Base<Any> zu gehen).

+0

Kann ich die Namen-Struktur Felder mit Aufzählungen verwenden? Meine realen Strukturen enthalten viele Felder mit Namen und mehreren Methoden. –

+0

Sie können beliebige Daten in enum-Varianten eingeben. Dies würde zum Beispiel funktionieren: 'struct Foo {f: uint}; enum FooBar {EFoo (Foo)} '. Enums unterstützen auch Felder in ihren Varianten (sogenannte struct-Varianten): 'enum FooBar {Foo {f: uint}}', obwohl dieses Feature gated ist und dies nur eine syntaktische Bequemlichkeit ist - struct-Variante ist keine Struktur und kann nicht haben Methoden zum Beispiel. –

+0

Nun, Rust * hat * die Fähigkeit, Typen zur Laufzeit zu prüfen - sie wird über 'Any'-Eigenschaft exponiert. 'AnyRefExt' hat' ist :: () 'Methode, die im Wesentlichen die gleiche Sache wie" instanceof "ist. Rust rät Rust jedoch zugunsten der statischen Typprüfungen ab. Es ist viel sicherer, einen Enum-basierten 'Match'-Versand zu schreiben, da es garantiert jeden möglichen Fall behandelt und statisch überprüft werden kann. –

3

Sie können meine match_cast Kiste verwenden:

match_cast!(any { 
    val as Option<u8> => { 
     format!("Option<u8> = {:?}", val) 
    }, 
    val as String => { 
     format!("String = {:?}", val) 
    }, 
    val as &'static str => { 
     format!("&'static str = {:?}", val) 
    }, 
}); 

match_down!(any { 
    Bar { x } => { x }, 
    Foo { x } => { x }, 
}); 
+3

Es gilt als gute Etikette zu [ offenbaren, wenn Sie etwas empfehlen, das Sie selbst erstellt haben] (http://meta.stackexchange.com/q/15787/281829) – Shepmaster

Verwandte Themen