I have the following Rust Playground permalink
which is from my following Ray Tracing in a Weekend.
At the point of implementing materials I chose to create a trait Material
.
My thinking was that objects in the world would have a material
attribute and upon a ray Hit
the Hit
can look at the object and ask for the material on demand. This is working fine for my Normal
trait which follows a similar thinking. I implemented this all with dynamic dispatch although I think I grasp enough to have done it statically with trait bounds as well.
In the linked code, you see line 13 I'm requesting those which implement Normal
have a method which will return a Material
. This then suggests that now Normal
is no longer eligible to be a trait object error[E0038]: the trait
Normalcannot be made into an object
If I understand correctly from such questions as this it seems that since generics are monomorphized the Rust runtime could no feasibly leak up the appropriate method for material
given there could ostensibly be one a different one for each type implementing Normal
? This doesn't quite click with me as it seems that, put in the same position as the Rust runtime, I would be able to look at the Normal
implementer I have in hand at a moment, say Sphere
and say I will look in Sphere
's vtable. Could you explain where I am wrong here?
From there, I tried to simply fight with the compiler and went to static dispatch. lines 17-21
struct Hit<'a> {
point: Vec3,
distance: f32,
object: &'a dyn Normal,
}
became
struct Hit<'a, T: Normal> {
point: Vec3,
distance: f32,
object: &'a T,
}
and from there I am left trying to plug hole after hole with what seems like no end in sight.
What design choices can I do differently in addition to learning what is fundamentally wrong with my current understanding?
I may be missing something, but I think you could - at least from what I've seen - follow your path further.
I think you could change this function:
fn material<T: Material>(&self) -> T;
As it stands, it says: Any Normal
offers a function material
where the caller can specify a Material
that the function will return.
But (I think) you want to state is: Any Normal
has a material that can be requested by the caller. But the caller has no right to dictate any Material
that will be returned. To translate this to code, you could state:
fn material(&self) -> &dyn Material;
This tells that material
returns a Material
(as a trait object).
Then, Sphere
could implement Normal
:
impl<'a> Normal for Sphere<'a> {
fn normal(&self, point: &Vec3) -> Ray {
Ray::new(point, &(point - &self.center))
}
fn material(&self) -> &dyn Material {
self.material
}
}