构造Rust特征的惯用方式

时间:2019-03-09 01:01:19

标签: rust

在“ Ray Tracer挑战”之后,我一直在Rust中编写Ray Caster,并且一直很难弄清楚在Rust中实现多态性的正确方法。我的首要任务是可以在多线程程序中使用该对象,这似乎是主要问题。

我有两种情况,但我只关注一种:形状。有多种形状(坚持使用able后缀,我最初将其称为特征Intersectable)。这是一个有效的特征对象实现,但不适用于多线程:

#[derive(Debug)]
pub struct Shape {
    pub parent: Option<Arc<Shape>>,
    pub transform: Matrix4,
    pub material: Material,
    pub intersectable: Box<Intersectable>,
}
pub trait Intersectable: Debug + IntersectableClone {
    fn local_intersect(&self, ray: &Ray, object: Arc<Shape>) -> Vec<Intersection>;
}

pub trait IntersectableClone {
    fn clone_box(&self) -> Box<Intersectable>;
}

impl<T> IntersectableClone for T
where
    T: 'static + Intersectable + Clone,
{
    fn clone_box(&self) -> Box<Intersectable> {
        Box::new(self.clone())
    }
}

impl Clone for Box<Intersectable> {
    fn clone(&self) -> Box<Intersectable> {
        self.clone_box()
    }
}

#[derive(Clone, Debug)]
pub struct Sphere {}

impl Intersectable for Sphere {
    fn local_intersect(&self, ray: &Ray, object: Arc<Shape>) -> Vec<Intersection> {
        ...sphere specific code
    }
}

#[derive(Clone, Debug)]
pub struct Plane {}

impl Intersectable for Plane {
    fn local_intersect(&self, ray: &Ray, object: Arc<Shape>) -> Vec<Intersection> {
        ...plane specific code
    }
}

作为纯结构,没有官方的多态性,我写了一种静态调度,看起来像这样:

#[derive(Debug, Clone)]
pub enum IntersectableType {
    Sphere,
    Plane,
}

#[derive(Debug, Clone)]
pub struct Intersectable {
    intersectable_type: IntersectableType,
}

impl Intersectable {
    pub fn local_intersect(&self, ray: &Ray, object: Arc<Shape>) -> Vec<Intersection> {
        match self.intersectable_type {
            IntersectableType::Sphere => self.local_intersect_sphere(ray, object),
            IntersectableType::Plane => self.local_intersect_plane(ray, object),
            _ => Vec::new(),
        }
    }

    fn local_intersect_sphere(&self, ray: &Ray, object: Arc<Shape>) -> Vec<Intersection> {
        ...sphere specific code
    }

    fn local_intersect_plane(&self, ray: &Ray, object: Arc<Shape>) -> Vec<Intersection> {
        ...plane specific implementation
    }
}

这很好用,但感觉很不生锈。我在使用其他实现时遇到了一些问题: -使用Box<Intersectable>(当它是Trait而不是结构时)很难克隆(我复制了How to clone a struct storing a boxed trait object?,但不喜欢使用'static,因为这使得并发成为不可能)。 -使用Arc<Intersectable>似乎和Box存在相同的问题,尽管也许有办法使之工作。

在Rust中有没有一种方法可以让我利用并发性,而不必像这样编写手动静态分派?

1 个答案:

答案 0 :(得分:0)

我也在做“光线追踪挑战”,虽然比你落后一点。我没有考虑并发性。我现在尝试遵循 A-Frame ECS 的想法:

pub trait Primitive {}

pub struct Shape<T: Primitive> {
    pub transformation: M4x4,
    pub material: Material,
    pub primitive: T,
}

pub struct Sphere;
impl Primitive for Sphere {}