Can you call iter() multiple times?

Are iterators separate from the types they're iterating or are they part of it?

Meaning, is the below reasonable:

let a = foo.iter();
let b = foo.iter();

Or are a and b the same iterator?

More specifically, can I pass a and b to separate threads and iterate over them separately?

Generally they are separate structs, though I'm not sure that what you mean. There are iterator which take ownership of the original data structure and there are iterators that borrow the original data structure.

By convention, an iter() method indicates a borrowing iterator that doesn't consume the collection, so that's usually reasonable (possible).

And in fact it indicates a shared borrow,[1] so both can be active at the same time.

There's nothing preventing some weird implementation, but generally speaking, they will have separate state and not interfere with each other. So long as they implement Send, you can send them to different threads and iterate them independently.


  1. by convention ↩ī¸Ž

1 Like

Yes. That's the whole point of iterators. If iteration state weren't decoupled from collections, then there would be no need for separate iterators.

As long as they do not result in shared mutability (i.e., the iterators are read-only), then yes.

But the best part of Rust is: you can just try it! If it would violate thread safety, then it won't even compile. So as long as it compiles, you know it's thread-safe.

This couldn't really be done well (and would be highly undesirable). This would mean that advancing a would silently advance b (and vice versa). Rust generally tries to fight against shared mutability, since shared mutability is an ample source of logic and safety bugs.

In general, when two things are separate in this language, there won't be spooky action-at-a-distance between them. This is one of the cornerstones of the design of this language.

I don't think OP is asking about that; I'm pretty sure the question is about the iteration state — which is necessarily different from the contents of the data structure, even in the case of an owning iterator.

It's not necessary, you can mix the two. It's a terrible idea, but possible.

But yeah, probably not the actual point, so I won't belabor the point beyond this comment.

1 Like

.iter() create a borrowed reference, thus it is safe to send it (in case the underlying item is Sync).

Some item cannot be sent in this way, (for example, Rc<RefCell<T>>, which cannot send to another thread) but others are fine.

Lifetimes on the Iterator trait prevent collections from returning references to their own elements. This is a hard guarantee enforced at compile time for all iterators. By-reference iterators are forced to always be a separate object.

Iterators are never meant to be implemented on the collection directly, and you can rely on this. Very often it's literally impossible to implement iterator on the collections, and even in cases where it could be doable technically (when returning owned elements by draining the collection empty), even that is not implemented by convention, and consuming/draining iterators are still always a separate type.

2 Likes

What I actually meant is that the iteration state is necessarily another, additional piece of information.

Even if you were to implement Iterator directly on a Vec or HashMap type itself, you would still somehow need to keep track of the next element. So the Vec or HashMap would, for example, need an additional index or pointer that would point to the next item. I don't think there's any way around that, whether or not you store that extra pointer in the same struct or a separate one.

(Realistically speaking – let's try to stick to O(n) and not start shifting all items to the front upon every call to next :D)

1 Like