Why `iter()` is not part of a trait?

All collections implement method iter(). But I don't find such trait and it surprises me.
Why there is not such trait?
Is that defect of design caused by history of development of design of Rust?
Or there is a good reason why should not be such a trait?

"All collections implement method iter()" - could you confirm that?


Should I ask the question on the internals forum?

1 Like

Any type Collection that has an iter method generally also has an IntoIterator implementation for &Collection, which does the same thing.

5 Likes

So you confirm my assumption? I thought so.

What about the original question?

The trait that would idiomatically be implemented by collections with an iter method is IntoIter, the same trait that also handles into_iter: In particular, in order for type T to support an operation like iter, you'd make sure that &'a T implements IntoIter<Item = &'a I> where I is the “owned” item type.

As for why iter is not part of a trait... well, it's probably mostly because it's established the way it is right now, and also perhaps because the most natural way to write such a trait would involve GATs. Nonetheless, for comparison, e. g. the par_iter method for rayon's parallel iterators is part of a trait which is automatically implemented for types T where &T implements the IntoParallelIterator trait.


By the way, everything discussed here for &T and iter methods also applies to &mut T and iter_mut methods.

5 Likes

Trying to answer my question: there is no such need for STD. In most cases you can pass iterator instead of passing collection, and it is enough. But can "most cases" be replaced by "all cases". Is that totally useless to have such a trait? If not, then developers which encounter a problem which requires such a trait should implement the trait by themselves for each STD collection? Is that harmful to have in STD a trait which is not totally necessary for STD, but is so for some problems developers encounter?

Is GAT still a pain point?

Nonetheless, for comparison, e. g. the par_iter method for rayon's parallel iterators is part of a trait which is automatically implemented for types T where &T implements the IntoParallelIterator trait.

Could you please rephrase, what is your point here?

That should never be necessary, as all such use cases should be covered by the existing &Collection: IntoIter implementations. The main point of having/introducing a trait for iter is that the convenience method iter would no longer need to be implemented manually for new collections, and also that you could use it in generic contexts where you currently need to write into_iter() (called on a reference type).

I don't know exactly what you're asking for here; the reason I brought it up is that I would personally guess that the lack of a GAT feature might have, historically, contributed why the standard library design doesn't have iter as a trait method. (I haven't checked if this guess of mine is true in any shape, though.)

Oh, just the fact that rayon has a trait for par_iter demonstrates that having a trait for iter can make sense / is feasible.

2 Likes
for item in &list { ... }
for item in list.iter() { ... }

foo(&list);
foo(list.iter());

fn foo<I, T>(iter: I)
where
    I: IntoIterator<Item = T>,
{}

Most collections with the iter method, I'm sure, have the impl IntoIter for &Collection which allows the same thing.

2 Likes

You meant interface of IntoIterator could be used without consuming it's content? Oh, didn't realize that. That surprised me!

IntoIterator always "consumes", but nothing stops it from being implemented for references, in which case it "consumes" a reference.

6 Likes

Note that rayon itself can't add par_iter(&self) as an inherent method on other types, so a trait is the tool we have. We could have left it for downstream users to add their own inherent par_iter methods, and they still can, but that would leave out upstream std collections.

1 Like

Yes, it did.

However, today I'm not sure we need it. IntoIterator is important for for loops, but abstracting over collection types has proven to be not so useful and even if it is, you can just say "a reference to it is IntoIterator<Item = &T>.

2 Likes

Indeed.