JSextonn
JSextonn

Reputation: 236

Implement and consume trait for another trait

I have a trait that I would like to be able to use as an iterator and I would like the iterator implementation to make use of the custom trait implementation. Is something like the below possible in rust, where two trait implementations are dependent on each other, or is a different architecture recommended?

trait Generator {
    fn generate(&self) -> String;

    fn generate_many(&self, count: usize) -> Vec<String> {
        self.take(count).collect()
    }
}

impl Iterator for dyn Generator {
    type Item = String;

    fn next(&mut self) -> Option<Self::Item> {
        Some(self.generate())
    }
}

EDIT:
Attempt with supertrait

trait Generator: Iterator {
    type Item = String;

    fn generate(&self) -> String;

    fn generate_many(&self, count: usize) -> Vec<String> {
        self.take(count).collect()
    }

    fn next(&mut self) -> Option<Self::Item> {
        Some(self.generate())
    }
}

Upvotes: 0

Views: 515

Answers (1)

user4815162342
user4815162342

Reputation: 155495

Ideally you'd write a blanket implementation of Iterator for anything that implements Generator, such as:

impl<T: Generator> Iterator for T {
    // ...
}

But Rust won't accept that because you can't provide blanket implementations of traits you didn't define in your own crate - that would violate the orphan rule.

However, nothing stops you from wrapping any Generator in your own generic type that implements Iterator:

struct Wrapped<G: Generator>(G);

impl<G: Generator> Iterator for Wrapped<G> {
   // ...
}

This doesn't violate the orphan rule because the blanket implementation only applies to variations of the Wrapped type that we've defined locally, and leaves T itself untouched. It is zero-overhead because Wrapped<G> just stores G directly, whatever G is, introducing no allocation or indirection.

Finally, you don't even need to implement the above struct, you can leave the wrapping to the iter::from_fn() helper provided by the standard library:

fn iter<G>(g: &G) -> impl Iterator<Item = String> + '_
where
    G: Generator + ?Sized,
{
    std::iter::from_fn(move || Some(g.generate()))
}

Then you can use iter() to implement generate_many():

fn generate_many(&self, count: usize) -> Vec<String> {
    iter(self).take(count).collect()
}

Runnable code in the playground.

Upvotes: 3

Related Questions