How do I create a by-value iterator on the stack?

2020-07-18 02:46发布

问题:

I can create a consuming iterator in the heap:

vec![1, 10, 100].into_iter()

I can also create an iterator on the stack that borrows the elements:

[1, 10, 100].iter()

But if I write this:

[1, 10, 100].into_iter()

This is not a consuming iterator because [T; _]::into_iter does not exist: IntoIterator is only implemented for the borrowed version (aka slice). Is there a simple way (preferably in the std lib) to create a consuming iterator on the stack?


I know that [1, 10, 100].iter().cloned() can be done, but this requires the items to be clonable.

回答1:

You can have a macro which wraps the values in a once iterator and chains them together:

macro_rules! value_iter {
    () => {
        std::iter::empty()
    };
    ($v: expr, $( $rest: expr ), +) => {
        std::iter::once($v).chain(
            value_iter!($($rest),*)
        )
    };
    ($v: expr) => {
        std::iter::once($v)
    };
}

Using:

#[derive(Debug, PartialEq)]
struct Foo;

let it = value_iter![Foo, Foo, Foo];

let all: Vec<_> = it.collect();
assert_eq!(all, vec![Foo, Foo, Foo]);

A known drawback is that the iterator will not be an exact-size iterator, and so the compiler might miss some obvious optimizations.

Playground



回答2:

Is there a simple way (preferably in the std lib) to create a consuming iterator on the stack?

No.

Is there a simple way (preferably in the std lib) to create a consuming iterator on the stack?

Yes. Use a crate like stack or smallvec, which provide array types that implement IntoIterator.



回答3:

Very ugly, but technically works:

for s in [
    Some(String::from("hello")),
    Some(String::from("goodbye"))
].iter_mut().map(|option| option.take().unwrap()) {
    let s: String = s;
    println!("{}", s);
}

You can use a macro that achieves this in a prettier way:

macro_rules! iter {
    [ $( $item:expr ),+ ] => {{
        [ $( Some($item), )+ ]
        .iter_mut()
        .map(|o| o.take().unwrap())
    }};
    // Rule to allow a trailing comma:
    [ $( $item:expr, )+ ] => {{
        iter![ $( $item ),+ ]
    }};
}

fn main() {
    for s in iter![String::from("hello"), String::from("goodbye")] {
        println!("{}", s);
    }
}


标签: iterator rust