I don't see anything in the documentation which requires any particular iteration order.
I would draw your attention to this part, however:
Repeated Access
The
Sequence
protocol makes no requirement on conforming types regarding whether they will be destructively consumed by iteration. As a consequence, don’t assume that multiplefor
-in
loops on a sequence will either resume iteration or restart from the beginning:for element in sequence { if ... some condition { break } } for element in sequence { // No defined behavior }
In this case, you cannot assume either that a sequence will be consumable and will resume iteration, or that a sequence is a collection and will restart iteration from the first element. A conforming sequence that is not a collection is allowed to produce an arbitrary sequence of elements in the second
for
-in
loop.
The problem isn't over-specification; it's the opposite. You should always be very cautious when you see the phrase "No defined behaviour".
(Note: this isn't C-style UB with nasal demons and the like: only 1 of 2 possible outcomes may occur for a valid conformance - either iteration will consume, or it won't, but which of those outcomes actually happens is not knowable from generic code. It might be better to call it "Unpredictable behaviour").
(Edit: Or is it? I'm not sure. That "arbitrary sequence of elements in the second for loop" bit would imply that more than those 2 outcomes may occur. Hmm... . Could it produce bogus object pointers? What if it was just a sequence of plain integers which your loop interprets as memory addresses? I guess it could lead to C-style UB).
The thing about this documentation is that it is too high-level: to really grasp what this means, you need to go back to the first sentence and remember that this applies to all iteration, and that iteration is the only way to get the Sequence
's elements (every method, like .prefix
or .first(where:)
, uses iteration, so they are interchangeable with the for
loops in the example). That's important because it's quite rare to write multiple consecutive for
loops over the same Sequence
. If you just read the above example, it might not be clear how this affects you and your code (not you personally, of course - Swift developers in general).
Essentially what this reduces to is that you can perform one and only one read operation on a Sequence
, and every subsequent operation returns . Either you call makeIterator()
and read via that iterator, or you call something like .first(where:)
, but not both. Otherwise the result is not predictable. If you pass the sequence in to a function, you have to assume that it might iterate it unless it is guaranteed not to (even logging/debugging code might iterate the sequence). Similarly, once you've done your read and possibly consumed the sequence (or not - who knows?), you must not pass that Sequence
as an argument to another function - otherwise their results also become unpredictable ("A conforming sequence ... is allowed to produce an arbitrary sequence of elements in the second for
- in
loop.").
All of which leads me to say that Sequence
is useless for writing generic algorithms. It might make a convenient entry-point, but all you should do in that function is get an iterator and pass that to your real implementation. Treat them as yucky, slimy things and touch them as little as you possibly can.
The underlying issue with Sequence
is that it's a hack. It exists for language convenience - using for
loops for single-pass sequences and allowing Collection
s easy access to algorithms written in terms of single-pass sequences. But from a conceptual/modelling perspective? It's all over the place. It achieves abstraction over single- and multi-pass sequences by just not defining its behaviour. A better design would likely require new (magic?) language features, but the current model is not fit for purpose IMHO.