Trait implementation for both a trait object and for direct implementors of the trait

▼魔方 西西 提交于 2019-12-28 04:20:09

问题


I have a struct that mostly encapsulates a vector:

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

I also have a simple trait which is also implemented for other structs:

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

I want to implement Solid for Group, but I want to be able to use Group both for lists of the same implementation of Solid and for lists of mixed implementations of Solid. Basically I want to use both Group<Box<Solid>> and Group<Sphere> (Sphere implements Solid).

Currently I am using something like this:

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

This works, but having line-for-line the same code twice can't be the idiomatic solution. I must be missing something obvious?

In my case I measure a notable performance difference between both trait implementations, so always using Group<Box<Solid>> isn't a great option.


回答1:


Implement your trait for all Box<S> where S implements your trait. Then you simply delegate to the existing implementation:

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

You'll also find that it can be useful to do the same for references:

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

All together:

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

The ?Sized bound allows the S to not have a size known at compile time. Importantly, this allows you to pass in trait objects such as Box<Solid> or &Solid as the type Solid does not have a known size.



来源:https://stackoverflow.com/questions/33041736/trait-implementation-for-both-a-trait-object-and-for-direct-implementors-of-the

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!