Traits in Rust are nicer than I imagined

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