Search code examples
genericsrusttraits

How to have a trait method have a struct argument, in which an item must implement that same trait?


I have a struct Lambertian:

pub struct Lambertian {
    albedo: Color,
}

A Material trait with an associated method:

pub trait Material {
    fn scatter(&self, ray: &Ray, rec: &HitRecord) -> Option<(Ray, Color)>;
}

And finally, another struct that holds a value of something that implements the Material trait

pub struct HitRecord<T>
where T: Material
{
    // snip
    pub mat_ptr: Rc<T>,
}

So, I want the generic T in HitRecord to implement Material, but I have a parameter of type HitRecord in the trait method scatter. To make this work, I have to annotate Material with another T, then specifying that that T implements Material as well... Hence doing a recursive definition. What do I need to do instead ?


Solution

  • You can put the generic on the scatter method rather than Material:

    pub trait Material {
        fn scatter<T>(&self, ray: &Ray, rec: &HitRecord<T>) -> Option<(Ray, Color)>
        where
            T: Material;
    }
    

    Or if you want rec to use the same material type as self, make the generic argument Self:

    pub trait Material {
        fn scatter(&self, ray: &Ray, rec: &HitRecord<Self>) -> Option<(Ray, Color)>;
    }
    

    This requires relaxing the constraint Sized constraint on HitRecord<T>:

    pub struct HitRecord<T>
    where
        T: Material + ?Sized,