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