I am trying to add a convenience method to the Vec<T>
type. I believe using a trait is the correct way to do this, but could be wrong. Below is what I have so far, which complains that
trait objects must include the `dyn` keyword
What am I doing wrong?
trait IndexWhere<T> {
fn index_where(&self, f: Fn((usize, T)) -> bool) -> Vec<usize>;
}
impl<T> IndexWhere<T> for Vec<T> {
fn index_where(&self, f: Fn((usize, T)) -> bool) -> Vec<usize> {
self.iter().enumerate().filter(f).map(|x| x.0).collect()
}
}
edit: removed bounds on T as I added them by accident
Alternatively this can also be implemented by not using a trait object, but a generic type instead:
trait IndexWhere<T> {
fn index_where<F>(&self, f: F) -> Vec<usize>
where
F: Fn(&(usize, &T)) -> bool;
}
impl<T> IndexWhere<T> for Vec<T> {
fn index_where<F>(&self, f: F) -> Vec<usize>
where
F: Fn(&(usize, &T)) -> bool,
{
self.iter().enumerate().filter(f).map(|x| x.0).collect()
}
}
Or even:
trait IndexWhere<T> {
fn index_where(&self, f: impl Fn(&(usize, &T)) -> bool) -> Vec<usize>;
}
impl<T> IndexWhere<T> for Vec<T> {
fn index_where(&self, f: impl Fn(&(usize, &T)) -> bool) -> Vec<usize> {
self.iter().enumerate().filter(f).map(|x| x.0).collect()
}
}
which is equivalent to the previous solution, but uses the impl
keyword instead.
You will find that the Rust standard library implements such cases using generic types and avoids trait objects. You can look at the Iterator::map
implementation as an example: https://doc.rust-lang.org/src/core/iter/traits/iterator.rs.html#682-685
Using trait objects has the side-effect of making your method use dynamic dispatch instead of static dispatch, which has the possible consequences listed in the documentation:
There is a runtime cost when this lookup happens that doesn’t occur with static dispatch. Dynamic dispatch also prevents the compiler from choosing to inline a method’s code, which in turn prevents some optimizations.