Collect - does it imply a move?

If I have a collection, say type B, that I want to build using collect (fn collect<B>(self) -> B), is it a given that I'm moving the items of the Iterator sourcing my collection into B?

My hunch is that the answer is yes given the need to keep the Item type fixed (which would include move semantics where T != &T). If so, I can't see how the following type declaration enforces the move? IntoIter is an idiomatic/conventional name for a struct, but not something the compiler can enforce?

pub trait IntoIterator
where <Self::IntoIter as Iterator>:: Item == Self::Item,
   type Item;
   type IntoIter: Iterator;
   fn into_iter(self) -> Self::IntoIter;

Thank you for any comments or clarifications on how I'm thinking about this.

- E

Iterator::next() returns a value of Option<<Self as Iterator>::Item>, so those values are being moved as the collection consumes the iterator in its FromIterator. In theory, the collection could do some other mapping along the way, but it at least consumed the Item.

In the other half of your question, IntoIterator normally implies moving items out of a collection, but there's nothing strictly enforcing this, and it sometimes means different things. IntoIterator for Vec<T> does move T values, but it could do something else, like produce new default values for T: Default. IntoIterator for &Vec<T> clearly can't move out of a vector it doesn't own, but it produces Item = &T values instead. You can still collect that, "moving" the references as they're produced, but it has no effect on the underlying Vec<T>.

1 Like

Good point. Iterator::next always moves whatever is in the iterator.

I agree with what you are saying. The key word being "normally". The transformation that can occur is expressed in the structs that implement Iterator. For instance, MyCollection's constructors of the "helper" structs that implement Iterator such as MyCollection::iter() -> Iter , MyCollection::iter_mut() -> IterMut. By convention, the items would be &'a and &'a mut respectively. In each of these examples we changed the type:

val -> &val
val -> &mut val

In the T -> B transformation, where we use the iterating capacity of T where T: IntoIterator, the only place I can see where that flexibility is restricted to a move is by specifying that the B::Item must be the same as T::Item.

pub trait FromIterator<A> {
   fn from_iter<T>(iter:T) -> Self
   where T: IntoInterator<Item = A>

So what:

  1. I can specify what gets moved by setting collect:B<here> (but a move is always what will happen per the observation for next)

  2. the sourcing collection T must have the same Item type as <here> (I can use map* to pre-process the items in T as required to make it so).

  1. I agree tha the use of IntoIter in the type spec is not the spec itself, but a reminder of the spec set by the item types not changing. The equality constraint is the enforcer. That's all I can see.

Thank you for your clarifying points. Let me know if I'm misunderstanding anything.

*map also moves items.

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