Возврат универсального типа через связанный тип

Я пытаюсь сделать что-то более общее, но не могу попробовать это со связанными типами. Даны черта Shape и тип Renderer, который пытается вернуть Builder для определенного вида формы.

форма

#[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;
}

строитель

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
    }
}

средство визуализации

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
    }
}

Я хочу добиться того, чтобы метод set_shape вернул RectangleBuilder , если данный shape имеет тип Rectangle


person xetra11    schedule 07.09.2016    source источник
comment
Нужны ли конструкторам параметры для инициализации нового экземпляра, или они должны быть построены без параметров?   -  person Francis Gagné    schedule 07.09.2016
comment
на самом деле да - бит - это своего рода реализация-заглушка - сначала нужно добиться перехода от рендерера к возвращающемуся построителю. Вы, наверное, уже догадались, что я пытаюсь создать здесь шаблон строителя.   -  person xetra11    schedule 07.09.2016
comment
Думаю, я неправильно сформулировал свой первый комментарий. Нужны ли вам параметры для построения конструктора или каждая реализация ShapeBuilder должна поддерживать конструкцию без параметров?   -  person Francis Gagné    schedule 07.09.2016
comment
строительство будет без параметров. Вызов set_origin() будет единственным способом установить состояние для строителя - исходную точку формы, которая будет визуализироваться на самом деле.   -  person xetra11    schedule 07.09.2016


Ответы (1)


Вам необходимо предоставить статический метод в трейте ShapeBuilder, который создает экземпляр нового Self. Статический метод - это просто метод без параметра self.

Вы можете добавить метод напрямую:

pub trait ShapeBuilder: Sized {
    type Renderer: ShapeRenderer;

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

или проявите смекалку и определите ShareBuilder как часть Default .

pub trait ShapeBuilder: Default {
    type Renderer: ShapeRenderer;

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

Затем в set_shape вы можете создать экземпляр ShapeBuilder:

impl <'a>Renderer<'a>{
    pub fn set_shape<T: Shape>(&self, shape: T) -> T::Builder {
        ShapeBuilder::new() // or Default::default()
    }
}
person Francis Gagné    schedule 07.09.2016
comment
В этом примере. При установке объекта Rectangle в качестве shape аргумента - будет ли ShapeBuilder::new() возвращать RectangleBuilder? Потому что я не понимаю, как он решает, будет ли возвращающийся Builder RectangleBuilder, поскольку вы просто возвращаете Self, а не что-то вроде Self::ShapeBuilder (это неправильный синтаксис, который я знаю) - person xetra11; 07.09.2016
comment
@ xetra11 Да. Self в определении признака означает конкретный тип, реализующий эту особенность. В данном случае RectangleBuilder. Вы также можете написать T::Builder::new() или <T::Builder as ShapeBuilder>::new() вместо просто ShapeBuilder::new(), если вам так понятнее. - person krdln; 07.09.2016
comment
А, ладно ... так что он неявно знает, что я говорю об ассоциированном Типе T, если я вызываю ShapeBuilder::new() - в значительной степени магия и очень плохая читаемость, вам тоже так не кажется? - person xetra11; 07.09.2016
comment
Если вы знаете правила, это не волшебство. :) А если серьезно, это всего лишь один экземпляр компилятора, который выводит тип выражения из типа возвращаемого значения, указанного в сигнатуре функции. Другой пример: если вы вернете результат collect() , то компилятор определит, какой тип коллекции вы хотите, по типу возвращаемого значения функции. - person Francis Gagné; 09.09.2016