2015-10-09 2 views
4

У меня есть структура, которая в основном инкапсулирует вектор:Черта реализации как для объекта признака и для прямых реализаторов признака

struct Group<S> { 
    elements: Vec<S> 
} 

У меня также есть простой признак, который также реализован для других структур:

trait Solid { 
    fn intersect(&self, ray: f32) -> f32; 
} 

Я хочу осуществить Solid для Group, но я хочу, чтобы иметь возможность использовать Group как для списков тех же реализации Solid и для списков смешанных реализаций Solid. В основном я хочу использовать как Group<Box<Solid>>, так и Group<Sphere> (Sphere реализует Solid).

В настоящее время я использую что-то вроде этого:

impl Solid for Group<Box<Solid>> { 
    fn intersect(&self, ray: f32) -> f32 { 
     //do stuff 
    } 
} 

impl<S: Solid> Solid for Group<S> { 
    fn intersect(&self, ray: f32) -> f32 { 
     //do the same stuff, code copy-pasted from previous impl 
    } 
} 

Это работает, но с линии-в-линии и тот же код дважды не может быть идиоматических решением. Я должен упустить что-то очевидное?

В моем случае я измеряю заметную разницу в производительности между обоими реализациями признаков, поэтому всегда использовать Group<Box<Solid>> - отличный вариант.

ответ

4

Реализуйте свою черту для всех Box<S> где S реализует вашу черту. Тогда вы просто делегировать существующую реализацию:

impl<S: Solid + ?Sized> Solid for Box<S> { 
    fn intersect(&self, ray: f32) -> f32 { 
     (**self).intersect(ray) 
    } 
} 

Вы также найдете, что это может быть полезно, чтобы сделать то же самое для ссылок:

impl<'a, S: Solid + ?Sized> Solid for &'a S { 
    fn intersect(&self, ray: f32) -> f32 { 
     (**self).intersect(ray) 
    } 
} 

Все вместе:

trait Solid { 
    fn intersect(&self, ray: f32) -> f32; 
} 

impl<S: Solid + ?Sized> Solid for Box<S> { 
    fn intersect(&self, ray: f32) -> f32 { 
     (**self).intersect(ray) 
    } 
} 

impl<'a, S: Solid + ?Sized> Solid for &'a S { 
    fn intersect(&self, ray: f32) -> f32 { 
     (**self).intersect(ray) 
    } 
} 

struct Group<S>(Vec<S>); 

impl<S: Solid> Solid for Group<S> { 
    fn intersect(&self, ray: f32) -> f32 { 
     42.42 
    } 
} 

struct Point; 

impl Solid for Point { 
    fn intersect(&self, ray: f32) -> f32 { 
     100. 
    } 
} 

fn main() { 
    let direct = Group(vec![Point]); 
    let boxed = Group(vec![Box::new(Point)]); 
    let pt = Point; 
    let reference = Group(vec![&pt]); 

    let mixed: Group<Box<Solid>> = Group(vec![ 
     Box::new(direct), 
     Box::new(boxed), 
     Box::new(Point), 
     Box::new(reference), 
    ]); 

    mixed.intersect(1.0); 
} 

Флаг ?Sized позволяет S не иметь размер, известный во время компиляции. Важно отметить, что это позволяет вам передать объекты-объекты, такие как Box<Solid> или &Solid, так как тип Solid не имеет известного размера.

+0

Что делает '? Sized', чтобы заставить его работать? –

+1

@CarlLevasseur Я обновил существующее предложение о '? Sized'; неужели это яснее? – Shepmaster

+0

Да, поэтому, если 'Box ' '' Solid', то использовать его в функции, которая принимает '& Solid', у нас было бы 2« перенаправления », это влияет на производительность? Кроме того, когда полезно реализовать его для ссылок? –

Смежные вопросы