Reputation: 236
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
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