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)
}
}