Using Iterators to produce more output values than inputs


#1

I want to iterate over a data source and produce more then one entry in the resulting iterator for each input value.

This is a heavily reduced example of my problem:

led_values.extend(iter.flat_map(
    |x: u64| [x as u16, x as u16, x as u16].into_iter()
));

That gives me:

error[E0597]: borrowed value does not live long enough
   --> src/main.rs:156:75
  |
2 | |x: u64| [x as u16, x as u16, x as u16].into_iter()
  |          ------------------------------           ^ temporary value dropped here while still borrowed
  |          |
  |          temporary value created here
3 | ));
  |  - temporary value needs to live until here

After hours of investigations I think I’ve now understood why this happens: into_iter is only implemented for &[T; N] and not [T; N] - which was completely unexpected to me!

Why is into_iter not implemented for [T; N]?
How can I solve the actual problem of mapping a single value to multiple result values?

Thanks for any hints!


#2

Check out the discussion of this limitation in RFC 2185. For now you need to pick some other iterator type to return in flat_map. For example Vec’s iterator would work.

led_values.extend(iter.flat_map(
    |x: u64| vec![x as u16, x as u16, x as u16]
));

#3

Thanks for pointing out the discussion.

The workaround involves frequent heap allocations, which I cannot afford in my case. Or will they be optimized out by the compiler?


#4

The zero allocation way would be to come up with your own iterator type. Playground

Or pick any standard library iterator that does not allocate. Playground


#5

I’d recommend using ArrayVec from the arrayvec crate.

led_values.extend(iter.flat_map(
    |x: u64| ArrayVec::from([x as u16, x as u16, x as u16])
));

should work?

It’s like Vec, but stack based. It doesn’t support all sizes, but it should support everything up to length 32, and it has a consuming IntoIterator just as Vec does.

ArrayVec<[u16; 3]> does have overhead compared to [u16; 3], but that overhead is a single usize representing the number of elements stored.


#6

That seems to be the best solution until const generics have landed (which seems to block RFC 2185).