1

I'm writing an extension trait on Iterator to show progress on the console:

for item in long_iterator.with_progress("Aligning dilithium matrix...") {
    ...
}

My goal is that if the Iterator also implements ExactSizeIterator, it will show a progress bar with a percentage; if not, it shows an indeterminate spinner instead.

I hoped I could just do this, and have the compiler figure out that the more "derived" implementation should take precedence, but alas:

pub struct ProgressBarIterator<I: Iterator> {
    inner: I,
    ...
}

impl<I: Iterator> Iterator for ProgressIterator<I> {
    ...
}

pub trait WithProgress<I: Iterator> {
    fn with_progress(self, message: &str) -> impl Iterator<Item = I::Item>;
}

impl<I: ExactSizeIterator> WithProgress<I> for I {
    fn with_progress(self, message: &str) -> impl Iterator<Item = I::Item> {
        ProgressBarIterator { ... }
    }
}

impl<I: Iterator> WithProgress<I> for I {
    fn with_progress(self, message: &str) -> impl Iterator<Item = I::Item> {
        ProgressBarIterator { ... }
    }
}

But alas, that's not how Rust works:

error[E0119]: conflicting implementations of trait `progress::WithProgress<_>`

Is there a way to make this work? Or am I best off with my current workaround of having two unrelated traits with differently named methods?

Thomas
  • 174,939
  • 50
  • 355
  • 478
  • 1
    There is [an extant RFC & long discussion on specialization](https://github.com/rust-lang/rust/issues/31844), but so far it's not supported. – Masklinn Oct 27 '20 at 09:33
  • Though note that there are already a bunch such libraries, and your design looks a fair bit like [Indicatif](https://docs.rs/indicatif/0.15.0/indicatif/index.html). – Masklinn Oct 27 '20 at 09:37
  • And... specialising on `ExactSizeIterator` seems superfluous anyway: ESI provides utility methods, but fundamentally an ESI is "just" an iterator where the bounds of the `size_hint` match, so you could simply use that information at runtime (`ExactSizeIterator::len` checks it anyway). FWIW [Indicatif just uses the upper bound. if one is provided](https://docs.rs/indicatif/0.15.0/src/indicatif/iter.rs.html#4-25). – Masklinn Oct 27 '20 at 09:40
  • @Masklinn Indeed I'm using `indicatif` to render the progress bar, but its [`ProgressIterator`](https://docs.rs/indicatif/0.15.0/indicatif/trait.ProgressIterator.html) trait is not as ergonomic as I'd like: you either use `progress_with` and have to specify the length yourself, or you use `progress` and are stuck with the default styling. – Thomas Oct 27 '20 at 09:43
  • I guess I'll just use `size_hint()` instead, but I think the more general question "is such specialization possible" remains open. – Thomas Oct 27 '20 at 09:44
  • Look at the first link provided. – Masklinn Oct 27 '20 at 11:06
  • @Masklinn You could post that as an answer ;) – Thomas Oct 27 '20 at 11:08
  • "its ProgressIterator trait is not as ergonomic as I'd like" might be worth opening an issue or PR against indicatif. – Masklinn Oct 27 '20 at 11:08

0 Answers0