Я писал Ray Caster в Rust после «The Ray Tracer Challenge», и мне было трудно найти правильный способ реализации полиморфизма в 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
}
}
Как чистая структура, без официального полиморфизма, я написал своего рода статическую диспетчеризацию, которая выглядит какthis:
#[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>
(когда это была черта, а не структура), трудно клонировать (я скопировал Как клонировать структуру, хранящую объект черты в штучной упаковке? но ему не нравилось использовать static, поскольку это делало параллелизм невозможным).- Использование Arc<Intersectable>
, похоже, имело те же проблемы, что и Box
, хотя, возможно, есть способ сделать эту работу.
Есть ли способ сделать это в Rust, который позволяет мне использовать преимущества параллелизмаа не писать ручную статическую рассылку вот так?