xetra11
xetra11

Reputation: 8885

Returning a generic type through an associated type

I try to get something more generic done but am failing on trying it with associated types. Given is a Shape trait and a Renderer type which tries to return a Builder for a specific kind of shape.

shape

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

pub trait Shape{
    type Builder : ShapeBuilder;
}

impl Shape for Rectangle{
    type Builder = RectangleBuilder;
}

builder

pub trait ShapeBuilder{
    type Renderer: ShapeRenderer;
    fn set_origin() -> Self::Renderer;
}

pub struct RectangleBuilder;

impl ShapeBuilder for RectangleBuilder{
    type Renderer = RectangleRenderer;
    fn set_origin() -> Self::Renderer{
        RectangleRenderer
    }
}

renderer

pub struct Renderer<'a>{
    canvas:&'a mut Canvas
}

impl <'a>Renderer<'a>{
    pub fn new(canvas:&'a mut Canvas) -> Renderer {
        Renderer{
            canvas: canvas
        }
    }
    pub fn set_shape<T: Shape>(&self, shape: T) -> T::Builder{
        // trying to return the specific builder through shape::Builder
        // or something like that
    }
}

What I want to achieve is that the set_shape method will return the RectangleBuilder if the given shape is of type Rectangle

Upvotes: 0

Views: 1839

Answers (1)

Francis Gagn&#233;
Francis Gagn&#233;

Reputation: 65927

You need to provide a static method in the ShapeBuilder trait that instantiates a new Self. A static method is just a method with no self parameter.

You can either add a method directly:

pub trait ShapeBuilder: Sized {
    type Renderer: ShapeRenderer;

    fn new() -> Self;
    fn set_origin() -> Self::Renderer;
}

or be clever and define ShareBuilder as a subtrait of Default.

pub trait ShapeBuilder: Default {
    type Renderer: ShapeRenderer;

    fn set_origin() -> Self::Renderer;
}

Then, in set_shape, you can instantiate the ShapeBuilder:

impl <'a>Renderer<'a>{
    pub fn set_shape<T: Shape>(&self, shape: T) -> T::Builder {
        ShapeBuilder::new() // or Default::default()
    }
}

Upvotes: 3

Related Questions