2016-12-27 9 views
3

У меня есть базовая структура, называемая Frame, которая полезна для кучи вычислений :.Простая организация признаков ржавчины для «полиморфного» возврата

pub struct Frame<T> { 
    grid_val: Vec<T>, 
    grid_space: Vec<[T; 2]>, 
    calculated_result: Option<Vec<T>> 
} 

Frame могут быть использованы для описания самых основных расчетов, но иногда есть более сложные вопросы, которые приходят и мне нужно добавить еще немного геометрическую информацию. Поэтому я использовал композиции для каждой геометрии:

pub struct Sphere<T> { 
    grid: Frame<T>, 
    radius: T 
} 

pub struct Hyperbola<T> { 
    top_grid: Frame<T>, 
    bottom_grid: Frame<T>, 
    internal_angle: T 
} 

Теперь у меня есть рабочая реализация Algorithm для Sphere:

pub trait Algorithm<T> { 
    fn calculate_something(&self) -> Result<Sphere<T>, Error> 
} 

impl Algorithm<T> for Hyperbola { 
    // do things with top_grid, bottom_grid, and internal_angle 
} 

impl Algorithm<T> for Sphere { 
    // do things with grid and radius 
} 

Это заполняет calculated_result и возвращает новый Sphere. Он реализован таким образом, потому что Algorithm нуждается в дополнительной геометрической информации для вычисления calculated_result - семантически, это имеет смысл для реализации в геометрии, результат которой связан с одним или несколькими Frame.

Я хочу реализовать те же самые Algorithm для Hyperbola. Фактически, он очень близок к тому же, и имеет смысл, чтобы черта была одинаковой, но для нее не имеет смысла возвращать Sphere<T>.

Я знаю, что могу добавить еще одну черту, например GeometricObject, и добавить еще один слой композиции, но это кажется чрезмерным. Думаю, я мог бы использовать Box, но это кажется неуклюжим.

Я также думал иметь calculate_something возвращающие Vec<T> вставлять вручную в зависимости от того, структура находится в использовании, но тогда эргономика возвращения того же типа структуры метод вызывается на разоряются (что отходы в общественном осущ /черта).

Как я могу организовать это, не делая его черты до конца?

ответ

4

Похоже, вы хотите ассоциированный тип:

pub trait Algorithm<T> { 
    type Output; 

    fn calculate_something(&self) -> Result<Self::Output, Error>; 
} 

impl<T> Algorithm<T> for Sphere<T> { 
    type Output = Sphere<T>; 

    fn calculate_something(&self) -> Result<Self::Output, Error> { 
     unimplemented!() 
    } 
} 

impl<T> Algorithm<T> for Hyperbola<T> { 
    type Output = Hyperbola<T>; 

    fn calculate_something(&self) -> Result<Self::Output, Error> { 
     unimplemented!() 
    } 
} 

Ассоциированные типы are described in detail в The Rust Programming Language. I настоятельно рекомендую прочитать всю книгу, чтобы ознакомиться с функциями, которые предлагает Rust.

Альтернативным решением является определение другого универсального типа на черта:

pub trait Algorithm<T, Out = Self> { 
    fn calculate_something(&self) -> Result<Out, Error>; 
} 

impl<T> Algorithm<T> for Sphere<T> { 
    fn calculate_something(&self) -> Result<Sphere<T>, Error> { 
     unimplemented!() 
    } 
} 

impl<T> Algorithm<T> for Hyperbola<T> { 
    fn calculate_something(&self) -> Result<Hyperbola<T>, Error> { 
     unimplemented!() 
    } 
} 

Вы должны решить When is it appropriate to use an associated type versus a generic type?

+0

Это интересно! Я действительно не понимал эту часть книги, потому что я был не готов к этому, но теперь у меня есть хороший контекст. –