2016-09-01 2 views
0

Ich probiere einige spezifische Builder-Muster aus. Bitte beachten Sie das Design hier - es ist ein Versuch.Matching auf Typen, die ein Merkmal implementieren und daher zurückgeben

Ich habe eine Renderer Art, die eine Methode set_shape ergibt. Der für den Aufruf angegebene Argumenttyp sollte das leere Merkmal IsRenderable implementieren. Die Anmeldung der Methode sollte sich dann zwischen den Strukturtypen Rectangle und Circle unterscheiden, die beide das Merkmal IsRenderable implementieren. Kümmere dich nicht um den zurückkehrenden Typ.

#[derive(Clone, Copy)] 
pub struct Rectangle { 
    pub origin: Point, 
    pub height: usize, 
    pub width: usize, 
} 

trait IsRenderable {} 

impl IsRenderBuilder for Rectangle {} 

impl<'a> Renderer<'a> { 
    // logic that needs lifetime 'a 
    pub fn set_shape<T: IsRenderable>(shape: T) -> Box<IsRenderBuilder> { 
     match shape { 
      _ => panic!("WTF!"), 
     } 
    } 
} 

Ich möchte so etwas tun. PSEUDO:

match shape { 
    Rectangle => return RectangleShapeBuilder, 
    Circle => return CircleShapeBuilder, 
    _ => panic!("WTF!"), 
} 

Antwort

5

Statt match zu verwenden, sollten Sie eine Methode auf Ihrem IsRenderable Eigenschaft hinzufügen, die die entsprechenden Builder für die jeweilige Implementierer zurückgibt.

#[derive(Clone, Copy)] 
pub struct Rectangle; 

pub trait IsRenderable { 
    fn new_builder(&self) -> Box<IsRenderBuilder>; 
} 

impl IsRenderable for Rectangle { 
    fn new_builder(&self) -> Box<IsRenderBuilder> { 
     Box::new(RectangleShapeBuilder) 
    } 
} 

struct RectangleShapeBuilder; 

pub trait IsRenderBuilder {} 

impl IsRenderBuilder for RectangleShapeBuilder {} 

pub fn set_shape<T: IsRenderable>(shape: T) -> Box<IsRenderBuilder> { 
    shape.new_builder() 
} 

Sie könnten sogar die IsRenderBuilder Boxen ein zugehöriges Art zu vermeiden, verwenden:

#[derive(Clone, Copy)] 
pub struct Rectangle; 

pub trait IsRenderable { 
    type Builder: IsRenderBuilder; 

    fn new_builder(&self) -> Self::Builder; 
} 

impl IsRenderable for Rectangle { 
    type Builder = RectangleShapeBuilder; 

    fn new_builder(&self) -> Self::Builder { 
     RectangleShapeBuilder 
    } 
} 

pub struct RectangleShapeBuilder; 

pub trait IsRenderBuilder {} 

impl IsRenderBuilder for RectangleShapeBuilder {} 

pub fn set_shape<T: IsRenderable>(shape: T) -> T::Builder { 
    shape.new_builder() 
} 
+0

Danke für den Tipp mit dem zugehörigen Typ! – xetra11

+0

Außerdem: Ich denke, der Rückgabetyp von 'set_shape' im zweiten Code-Snippet sollte' :: Builder 'sein, um mögliche Namenskonflikte zu vermeiden? –

+2

@LukasKalbertodt: Ich glaube nicht, dass Namenskonflikte ein Problem sind, da 'T :: Builder 'statisch aufgelöst ist, um" den assoziierten Typ 'Builder' in der Eigenschaft' IsRenderable' "zu bedeuten, wie alle Compiler über' T' wissen ist, dass es 'IsRenderable' implementiert. Rust funktioniert nicht wie C++ - Vorlagen, bei denen abhängige Typen nur zur Instanziierungszeit der Vorlage aufgelöst werden. Das Hinzufügen einer anderen Merkmalsbindung kann zu einer Mehrdeutigkeit führen, kann aber dann behoben werden, wenn die Mehrdeutigkeit erhöht wird, anstatt direkt zur Schwergewichtssyntax überzugehen. –

Verwandte Themen