spease
spease

Reputation: 766

How to return a boxed clonable iterator in Rust?

What's the right way to return a boxed clonable iterator from an expression? For example:

fn example() -> Box<Iterator<Item = String> + Clone> {
    unimplemented!()
}

This gives me an error that only automatic traits can be specified in this manner:

error[E0225]: only auto traits can be used as additional traits in a trait object
 --> src/main.rs:1:47
  |
1 | fn example() -> Box<Iterator<Item = String> + Clone> {
  |                                               ^^^^^ non-auto additional trait

This is my real code:

let my_iterator = {
    if a {
        Box::new(/* ... */) as Box<Iterator<Item = String> + Clone>
    } else {
        Box::new(/* ... */) as Box<Iterator<Item = String> + Clone>
    }
};
let pb = ProgressBar::new(my_iterator.clone().count() as u64);

If alternative suggestions are considered: the two branches represent one path for loading from a file, the other path for generating automatically, and I'd rather not keep things in memory if they aren't needed.

Upvotes: 4

Views: 592

Answers (1)

Shepmaster
Shepmaster

Reputation: 430891

Iterator is a trait and thus Box<Iterator> is a trait object.

Clone cannot be made into a trait object because it requires knowledge of Self, so we follow the instructions in How to clone a struct storing a boxed trait object?:

trait CloneIterator: Iterator {
    fn clone_box(&self) -> Box<CloneIterator<Item = Self::Item>>;
}

impl<T> CloneIterator for T
where
    T: 'static + Iterator + Clone,
{
    fn clone_box(&self) -> Box<CloneIterator<Item = Self::Item>> {
        Box::new(self.clone())
    }
}

fn example(switch: bool) -> Box<CloneIterator<Item = String>> {
    let i = vec!["a".into(), "b".into()].into_iter();
    if switch {
        Box::new(i)
    } else {
        Box::new(i.take(1))
    }
}

fn main() {
    let i = example(true);
    let i2 = i.clone_box();

    let j = example(false);
    let j2 = j.clone_box();
}

See also:

Upvotes: 2

Related Questions