Well, “trivial benefit” is perhaps even an understatement, the TLDR would be: “it’s impossible for .nth(n)
to have a non-consuming behavior on Iterator
”. The reason is a “mathematical” kind of argument. The relevant question to ask here is: “what is an str::iter::Iterator
”?
If you’re familiar with traits in Rust, you’ll know there’s two sides to traits. They are used on generic parameters, e.g. in a function foo<T: Iterator>(x: T) …
to add a “constraint” on T
. This constrains any uses foo(y)
to y
s of a type that implements Iterator
. On the other side there is the definition of the function foo
. In the body of foo
the “constraint” is more of a gain, enabling all the methods that Iterator
comes with to be called on the parameter x
.
It’s generally an all-or-nothing kind of thing: Either a type implements Iterator
which means there needs to be a definition for all the methods of Iterator
or it doesn’t, which means you can’t call your foo
on it, even when the methods the type cannot support are not even used in foo
.
As to not to force users of a trait, like the person defining foo
, to over-constrain their generic parameters, traits are usually defined to be quite minimalist actually: In essence this is true for Iterator
as well. Most methods of Iterator
, including nth
, really only are part of the trait to (a) allow nice method-syntax and (b) to allow a type to provide a more efficient implementation than the default one, if the type allows it. But conceptually, Iterator
has really only one method: fn next(&mut self) -> Option<Self::Item>
.
This is important to keep in mind when thinking about the design of the Iterator
API: Any type that defines next
with the correct signature is a full-fledged iterator. Every additional method in the API must be definable in terms of next
.
Now we can start to get mathematical, my initial statement becomes “it’s impossible for .nth(n)
to have a non-consuming behavior on Iterator
, because such an .nth(n)
cannot be defined in terms of .next()
only”. The exact argumentation as to why that is true would usually be done only on an intuition level:
The method .nth(n)
should return the n
th element. In order to even “get to” the n
th element, you need to call .next()
at least n
times, but as soon as you’ve done that, you already consumed at least the first n
elements (since .next()
does consume the next element).
With this in mind (“you have to consume at least the first n
elements”) then, according to its documentation .nth(n)
takes the least destructive approach possible: It stating “all preceding elements, as well as the returned element, will be consumed” means that exactly the first n
elements are consumed, in other words: as few elements as possible are consumed (one could imagine as an alternative that e.g. the whole iterator could be consumed, too).