特征对象和特征的直接实现者的特征实现

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

我有一个主要封装向量的结构:

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

我还有一个简单的特性,它也适用于其他结构:

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

我想为 Group 实现 Solid,但我希望能够将 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>> 并不是一个好的选择。

为所有 Box<S> 实施您的特质,其中 S 实施您的特质。然后你可以委托给现有的实现:

impl<S: Solid + ?Sized> Solid for Box<S> {
    fn intersect(&self, ray: f32) -> f32 {
        (**self).intersect(ray)
        // Some people prefer this less-ambiguous form
        // S::intersect(self, ray)
    }
}

您还会发现,对参考文献执行同样的操作也会很有用:

impl<S: Solid + ?Sized> Solid for &'_ S {
    fn intersect(&self, ray: f32) -> f32 {
        (**self).intersect(ray)
        // Some people prefer this less-ambiguous form
        // S::intersect(self, 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)
        // S::intersect(self, ray)
    }
}

impl<S: Solid + ?Sized> Solid for &'_ S {
    fn intersect(&self, ray: f32) -> f32 {
        (**self).intersect(ray)
        // S::intersect(self, 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<dyn Solid>> = Group(vec![
        Box::new(direct),
        Box::new(boxed),
        Box::new(Point),
        Box::new(reference),
    ]);

    mixed.intersect(1.0);
}

?Sized 绑定允许 S 在编译时不知道大小。重要的是,这允许您传入 特征对象 ,例如 Box<dyn Solid>&dyn Solid,因为类型 Solid 没有已知大小。

另请参阅:

  • What makes something a "trait object"?