I have rather questions like: Why all.inter().enumerate() and not just all.enumerate() ? Why for a in all.iter() and not simply for a in all ?
Apparently is the language clever enough to match the types (which is good), why isn't than simply clever enough to recognize then that in on the array needs the iteration? Why isn't possible to directly enumerate the array?
Even (at least this one) C++ has something simple:
for( auto y : x ) { // Copy of 'x', almost always undesirable
cout << y << " ";
}
for( auto &y : x ) { // Type inference by reference.
// Observes and/or modifies in-place. Preferred when modify is needed.
cout << y << " ";
}
for( const auto &y : x ) { // Type inference by reference.
// Observes in-place. Preferred when no modify is needed.
Not even "in" keyword is used there but it still allows iterations through anything that can be iterated without writing too much red tape, by invisibly expanding to these famous STL declare iterator with the unnecessary long declaration, iterate between begin and end constructs:
range-based for:
Automatically recognizes arrays.
Recognizes containers that have .begin() and .end().
Uses argument-dependent lookup begin() and end() for anything else
> Why all.iter().enumerate() and not just all.enumerate() ?
enumerate is a method of the Iterator trait, so you need an iterator. This way it can have a truckload of reusable methods without polluting every collection's namespace I guess, and still things work for collections with multiple iterators (just ask for the right one).
The iteration method does not have to be called ``iter`` I'd think (e.g. a tree structure might provide both a depth-first and a breadth-first iterator, none of which would be accessed via `iter`?)
See pcwalton's answer in an other subthread, an Iterable trait (which for would use instead of Iterator) was tried and didn't work out previously, but it's not shelved.
> for (x,y) in all.enumerate() does .iter.enumerate()
That makes literally no sense. What if enumerate() is the thing returning an iterable because reasons? Now it calls enumerate() on the wrong object, or blows up because there's no .iter in the first place.
You could have 30 methods on the Iterable trait (or directly on your vector) which would just delegate to the corresponding Iterator. Not sure there's that much value in it.
> for x in all.depthfirstiter()
Except according to your previous declaration that would result in a call to `all.iter.depthfirstiter()`. Or the compiler has to go magic things around, traverse the chain until it gets the first (or last? Or all of them? How do you pick which one's right?) Iterable, an inject an iter() calls in it…
enumerate() also has the advantage of guaranteed elimination of bounds checks. In general, iterators are Rust's solution for achieving C-like performance for common patterns with safety and without bounds checking.
You'd have to overload "for" to detect whether the expression implemented Iterable or Iterator to decide what to do, and that was felt to be too complex (e.g. what if you implement both?) There was an attempt to have a unified Iterable trait at one point [1], but I believe it didn't work with the trait matching rules.
Yeah, I would rather have "for" defined only to operate on Iterables. If we modified the trait matching rules we could probably do that backwards compatibly post 1.0.
Throwing some thoughts on this entire conversation from the perspective of someone who's been using C++ for a long time:
One of the reasons why the C++ for (:) syntax above feels like such an improvement over the traditional iterator for loop is that it cuts out a lot of noise that's the same for almost every container loop you ever do. The same objection could be raised as in another branch of this conversation, that you might have other kinds of ranges on an object, and I've even done that before (as obj.something_begin() and obj.something_end()). But even before ranged-for came it almost always turned out ugly and I regretted it.
If most uses of for in Rust call iter() (I don't know that they do, my experience with it thus far is really minimal, but it does seem common so far), it seems like a case where use should lead design and the extra noise cut out.
Basically, if I have more than one kind of range on an object I'd probably rather have functions on the object that return the 'unusual' ranges for the iteration. So rather than having:
for i in obj.iter() # the normal thing that I use all the time
for i in obj.bfs_iter() # something I use sometimes to be more explicit
for i in obj.dfs_iter() # another thing I use sometimes
I'd rather have:
for i in obj # the normal thing that I use all the time
for i in obj.breadth_first() # something I use sometimes to be more explicit
for i in obj.depth_first() # another thing I use sometimes
Or if there is no sensible default, to be forced to name the thing I actually need.
It seems entirely sensible to me to expect the for loop construct to take an Iterable object. It actually makes more sense to me than for it to take an iterator directly, conceptually.
On the question of 'what if both?' I'd propose making it so hard (or impossible?) to do that. If for took Iterable, and Iterators also implemented Iterable as "return this", there'd really be no concept of having both (except in that if you have an iterator, it is always both) and for itself would be left fairly uncomplicated while still being able to take both Iterable and Iterator objects.
I'm probably missing some reason why this isn't an obvious way to go, as a definite Rust newb, but it would feel natural to (my C++-rotted perhaps) brain. Also I know there's no language changes until post-1.0, so for now it is what it is.
[edit] Huh. Ok, re-reading it looks like this was almost all covered ground. Never mind.
I actually used enumerate in an earlier version of this post, but in the case I used it didn't work (pointed out to me on /r/rust). I'm going to go into enumerate and other iterator functions in future posts, so left it out for now. I agree this is a better formulation.