Vec<String> into impl Iteratator<&str> + 'static

Vec::iter() yields &str, but does not transfer ownership.

Vec::into_iter() transfers ownership, but yields &String.

i want to transfer ownership to the iterator, but i want the iterator to then hold that ownership, only providing a borrow.

vec.iter().map(|x|x.as_str()) doesn't work because of limitations of the borrow checker.

it seems like this would need some sort of special OwningIterator trait with a
fn borrow_next<'a>(&'a self) -> Option<&'a Self::Item>.

my question is thus: is it possible to do this with the standard library Iterator trait, and if not, is there a library that defines a trait that would make this work?

bonus question: would it be possible to define an trait where the next item can only be obtained once the previous one was dropped? this could be useful for a version of Lines that reuses an internal buffer for each line instead of creating a new allocation. i suppose this is possible if you just pass a callback, but i wonder if there's some other way, perhaps with some advanced type level state machines.

Not really relevant to the topic

Vec::<T>::iter() yields &T. T has to be sized to be in a Vec, so there's not one of those that yields &strs.

Vec<T>::into_iter yields T, so it only yields &String if you have a Vec<&String>.

The Iterator trait doesn't support handing out borrows of things that it owns. That's called a "lending iterator"[1] and requires a GAT.

We might get one in std some day, but we don't have one now.

Here's one by someone I trust, though I haven't used that crate specifically. There's a few others but I don't have a specific recommendation.

With a typical GAT based lending iterator, you can't call next until you've dropped the current item, so that's what you want. The reason being this signature:

fn lend_next<'a>(&'a mut self) -> Self::Item<'a>

If you had this signature instead:

// &self not &mut self
fn lend_next<'a>(&'a self) -> Self::Item<'a>

then it would still be lending, but you could hold on to multiple items. (But the implementation would need interior mutability to change state.)


  1. or sometimes "streaming iterator", but that's easy to confuse with async things ↩ī¸Ž

2 Likes

I guess you meant String without an ampersand?

And here maybe rather vec.into_iter().map(|x|x.as_str()) with into_iter() instead of iter()?

Like, it absolutely does? It doesn't transfer ownership, though.

If you meant .into_iter(), then that's not a "limitation of the borrow checker". Handing out references to items that are dropped would be wrong (is a UaF bug), and the borrow checker couldn't possibly allow it.

No. Just read its declaration:

trait Iterator {
    type Item;

    fn next(&mut self) -> Option<Self::Item>;
}

It says that next must yield elements of type Self::Item, and Self::Item doesn't in any way depend on the lifetime of self. Hence, the trait simply doesn't allow you to express this relationship.

for completeness, there's also internal-iterator, which i believe would also allow an iterator with an internal buffer, albeit in a slightly less flexible way.