Breaking news
Recently, I discovered that, besides serving as an interface, a trait in Rust can help create decoupled decorators. Two things really caught my eye this week.
One is the progress method used on an iterator by the indicatif crate. I include the ProgressIterator trait, and I can call progress() on an Iterator.
use indicatif::ProgressIterator; for _ in (0..file_count).progress() { //... }
The other: anyhow offers a context() method from its Context trait, to attach details to a Result.
use anyhow::Context; func().context("error details")?;
Just by including the traits, their methods are available for some types. I imagined that there are implementations of those traits for some specific types, but I had no idea how they are done.
What I want to get to
It was clear that I needed to create a trait that has the method I want to implement, and to implement the trait for the type I want it to decorate. I chose to decorate iterators with a count_where method that accepts a predicate and counts the elements that meet the condition implemented in the predicate.
#[cfg(test)] mod tests { use super::*; #[test] fn count_where() { let event_number = vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9].into_iter().count_where(|number| number % 2 == 0); assert_eq!(event_number, 5) } }
Step by step
I created a CountWhere trait that works with types that implement the Iterator trait.
This trait has a method count_where that accepts a predicate P and returns a number of elements (usize).
The predicate accepts an argument with type reference to the type that the iterator’s elements are (&Self::Item) and returns a bool (to indicate whether the current element should be counted or not).
pub trait CountWhere: Iterator { fn count_where<P>(self, predicate: P) -> usize where P: Fn(&Self::Item) -> bool; }
And I needed to provide the implementation for iterators, which is very simple: I just filter the iterator by the given predicate and count the elements.
impl<I> CountWhere for I where I: Iterator, { fn count_where<P>(self, predicate: P) -> usize where P: Fn(&Self::Item) -> bool, { self.filter(predicate).count() } }
Next
While the idea is simple, I still feel there are more details to dig into. I need to know what I don’t know.
pub trait CountWhere: Iterator { fn count_where<P>(self, predicate: P) -> usize where P: Fn(&Self::Item) -> bool; } impl<I> CountWhere for I where I: Iterator, { fn count_where<P>(self, predicate: P) -> usize where P: Fn(&Self::Item) -> bool, { self.filter(predicate).count() } } #[cfg(test)] mod tests { use super::*; #[test] fn count_where() { let event_number = vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9].into_iter().count_where(|number| number % 2 == 0); assert_eq!(event_number, 5) } }