I have the following code (playground):
trait Trait {
fn do_stuff(&self) -> bool;
}
impl<T: AsRef<str>> Trait for [T] {
fn do_stuff(&self) -> bool {
helper(self)
}
}
impl<T: AsRef<str>> Trait for Vec<T> {
fn do_stuff(&self) -> bool {
helper(self)
}
}
fn helper<T: AsRef<str>>(array: &[T]) -> bool {
// Create an iterator and do useful things with it,
// eventually returning a boolean value.
// I don't think details matter here.
true
}
It works fine, but the two implementations are exactly identical and the only reason for helper
's existence is to avoid the code duplication. I would like to know if there is a nice way to fold them into a single implementation.
My first attempt was the following (playground):
impl<T: AsRef<str>, U: AsRef<T>> Trait for U {
fn do_stuff(&self) -> bool {
helper(self.as_ref())
}
}
But the compiler complains about an unconstrained type T. Question 1: why is it considered unconstrained, given that T is still used as a bound to U? And why could it be a problem in this case?
Since helper()
only works on an iterator, I thought about implementing the trait for IntoIterator
instead:
impl<T: AsRef<str>, I: IntoIterator<Item=T>> Trait for I {
fn do_stuff(&self) -> bool {
helper(self.into_iter())
}
}
fn helper<T: AsRef<str>>(mut iter: impl Iterator<Item=T>) -> bool {
// Use the iterator and do useful things with it,
// eventually returning a boolean value.
true
}
But this doesn't compile, because I would need a non-consuming version of IntoIterator
, which doesn't seem to exist. Question 2: why isn't iter()
a trait implementation, like into_iter()
?
Finally, another option would be to implement the trait for Iterator
and make the trait consume its self
parameter, but this changes the usage pattern: callers first have to call .iter()
(for example), which is less discoverable.
Question 3: is there another way to achieve this? I don't really mind keeping the original code, but it's a nice learning exercise for me