Is it possible to require that a type implementing the current trait has another type implenting another trait with the first type?

I am doing some basic ray tracing and currently wanted to have this basic code:

/// A ray-hittable or drawable object.
pub trait Hittable: Origin + TransformMatrix /*+ Hits<Self> ???*/ {}

/// Returns only hits, not all intersections.
pub trait Hits<'a, Object> {
    /// Returns all the hits.
    fn hits(&self, object: &'a Object) -> Vec<Intersection<'a, Object>>;
}

impl<'a, T, Object> Hits<'a, Object> for T
where
    T: IntersectionsWith<'a, Object>,
{
    fn hits(&self, object: &'a Object) -> Vec<Intersection<'a, Object>> {
        self.intersections_with(object)
            .into_iter()
            .filter(|v| v.value >= 0.0)
            .collect()
    }
}

impl<'a> IntersectionsWith<'a, Sphere> for Ray {
    fn intersections_with(&self, object: &'a Sphere) -> Vec<Intersection<'a, Sphere>> {
        self.distance_to_intersections(object)
            .into_iter()
            .map(|value| Intersection { object, value })
            .collect()
    }
}

Here, in this basic code, there is a Ray type which implements Hits<'a, T> for T: Spherical + Origin + TransformMatrix (eventually, through some other traits) because a ray requires knowing these properties of an object for intersecting (for now). Now, I want to create a world containing a vector of such objects and wanted to come up with some common marker trait like Hittable or Drawable which means that if this trait is implemented then a ray can actually be cast to this object and checked for intersections.

Is it possible for one trait A to require that some other type (Ray) implements trait B with the type for type A?

Ideally, it would look something like:

/// A ray-hittable or drawable object.
pub trait Hittable: Origin + TransformMatrix + Hits<Ray, Self> {}

where Hits<Ray, Self> means there is an implementation of Hits<T> for Ray where T is Self, where Self implements Hittable:

// Hits<Ray, Self> means there is
impl Hits<Sphere> for Ray { }
impl TransformMatrix for Sphere { }
impl Origin for Sphere { }
impl Hittable for Sphere { }

What do you mean by "the type has a type" and by "implementing a trait with a type"?

I myself am confused already :slight_smile: What part are you referring to exactly?
By "implementing a trait with a type" I meant that there are two types - A and B and a trait C<T>, and then type A implements C<T> where T is B:

struct A;
struct B;
trait C<T> { }
impl C<B> for A { }

So, what I want to do is to write a marker trait that requires that there is a impl C<Self> for A somewhere where Self is this marker trait I want to write.
I think it will eventually be something like having the Into<U> implemented by having implemented From<T> - we only have From<T> implemented and the Into<U> we have automagically.

The first occurrence is the title itself ("a type implementing the current trait has another type"), and the second occurrence is here:

Self is a type, so it can't be a trait. Are you perhaps looking for the following?

impl<T> Marker<A> for T where A: C<T> {}

Or even more generally, abstracting over the concrete type A:

impl<T, U> Marker<U> for T where A: C<U> {}

Looking at your original snippet, you might want to try:

pub trait Hittable: Origin + TransformMatrix where Ray: Hits<Self> {}
2 Likes

Thanks a lot! And Rust is awesome! Though, I couldn't think of even trying to specify it like where ConcreteType: Trait<T>, felt like it wouldn't be "correct" :smiley:

Well, where is the fundamental language construct for specifying constraints. Even "supertrait" constraints are just syntactic sugar for where Self: Supertrait.

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.