rustiteratordynamic-dispatch

Extending Iterator for dynamic dispatch


I want to extend the Iterator trait with some convenience function. For example

trait BetterIterator: Iterator { 
    fn skip_bad(&mut self); 
}

This can be achieved like this:

struct Iter {}

impl Iterator for Iter {
    type Item = ();
    fn next(&mut self) -> Option<()> { None }
}


impl BetterIterator for Iter {
    fn skip_bad(&mut self) {}
}

fn make_iter() -> Box<dyn BetterIterator<Item=()>> {
    Box::new( Iter {} )
}

fn main() 
{
    let mut iter = make_iter();
    iter.skip_bad();
    for _item in iter {
        // ...
    }
}

What is unusual, here, is the dynamic dispatch in make_iter.

Now, it would be much nicer if skip_bad could be chained, e.g.

    for _item in make_iter().skip_bad() {
        // ...
    }

That means BetterIterator becomes

trait BetterIterator: Iterator { 
    fn skip_bad(&mut self) -> &mut Self; 
}

impl BetterIterator for Iter {
    fn skip_bad(&mut self) -> &mut Self { self }
}

But then the compiler complains "the trait BetterIterator cannot be made into an object".

Is it correct that the violated rule in Object Safety is "Dispatchable functions require: ...Be a method that does not use Self except in the type of the receiver."?

There is no way out of this, is there?


Solution

  • If dynamic dispatch is your goal you can return a mutable reference to a dyn BetterIterator instead.

    trait BetterIterator: Iterator {
        fn skip_bad(&mut self) -> &mut dyn BetterIterator<Item = Self::Item>;
    }
    
    impl BetterIterator for Iter {
        fn skip_bad(&mut self) -> &mut dyn BetterIterator<Item = Self::Item> { self }
    }