"Slotting" an extra element into an iterator without changing its ownership

In the following code I'm essentially trying to create an iterator over b except with the a array slotted in at the start of that iterator (playground link):

let a = vec![1,2,3];
let b = vec![ vec![4,5,6], vec![7,8,9], vec![10,11,12] ];
[a].iter().chain( b.iter() ).for_each( |v| println!("{:?}", v) );
println!("{:?}", a);   // borrow of moved value: `a`

The problem is that [a].iter()... takes ownership of a, and line 4 tries to print a - hence the error.

But if I write [&a] instead of [a], then the two iterators that I'm chaining have different element types (&Vec vs Vec) and so the compiler doesn't let me do this:

expected type &std::vec::Vec<{integer}>, found type &&std::vec::Vec<{integer}>

Is there a simple way to handle a situation like this? I just want to "slot" an extra element into the iterator, and then iterator over it. If you have any "general" lessons about dealing with this sort of thing, or links to articles/videos/chapters for me to look at, I'd be really grateful. Thanks!

You can't defy physics, so to speak. If you want to use the element even after adding it to the iterator, then you have to preserve it in some way, for example by doing either of the following:

  1. Adjust the entire iterator to operate over references. It seems to me that this solution would fit you because you use iter() anyway.
  2. Clone the first element and chain it before the iterator, taking ownership of the clone.
  3. Make the element Copy to perform alternative #2 implicitly (not an option for Vec)
  4. Make the iterator decide dynamically whether each item is owned or borrowed, for example you could iterate ower Cows in this case.

Side notes:

  1. use iter::once() for producing a singleton iterator instead of [a].iter(). The latter doesn't work with non-cloneable types anyway, because &[T] only implements Iterator<Item=&T>, not Iterator<Item=T>. (Or you could write vec![a].into_iter(), but that incurs a superfluous heap allocation.)
  2. .chain() accepts an IntoIterator, not only an Iterator, so instead of .chain(b.iter()), you can write .chain(&b), and instead of .chain(b.into_iter()), you could write .chain(b) directly.

So, to sum up: if you don't need owning access to any of the items, write

use std::iter::once;

let a = vec![1,2,3];
let b = vec![vec![4,5,6], vec![7,8,9], vec![10,11,12]];
once(&a).chain(&b).for_each(|v| println!("{:?}", v));

Or, if you sometimes want ownership, but not in the case of the first element:

use std::iter::once;
use std::borrow::Cow;

let a = vec![1,2,3];
let b = vec![
    Cow::Owned(vec![4,5,6]),
    Cow::Owned(vec![7,8,9]),
    Cow::Owned(vec![10,11,12]),
];
once(Cow::Borrowed(&a)).chain(b).for_each(|v| println!("{:?}", v));
3 Likes

This is commonly used to adapt a single value into a chain of other kinds of iteration. Maybe you have an iterator that covers almost everything, but you need an extra special case. Maybe you have a function which works on iterators, but you only need to process one value.

Exactly what I needed! Thank you! I really appreciate the time you took to explain the alternatives as well :grinning:

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.