Scala documentation on Iterator[T] here says the following:
It is of particular importance to note that, unless stated otherwise, one should never use an iterator after calling a method on it. The two most important exceptions are also the sole abstract methods:
next
andhasNext
.
They also give a specific example of safe and unsafe use:
def f[A](it: Iterator[A]) = {
if (it.hasNext) { // Safe to reuse "it" after "hasNext"
it.next // Safe to reuse "it" after "next"
val remainder = it.drop(2) // it is *not* safe to use "it" again after this line!
remainder.take(2) // it is *not* safe to use "remainder" after this line!
} else it
}
Unfortunately I don't follow the idea of unsafety here. Could someone shed some light for me here?
Here's a concrete example:
def eleventh[A](xs: Iterator[A]) = {
xs.take(10).toList
xs.next
}
We can try it out:
scala> eleventh((1 to 100).toList.toIterator)
res0: Int = 11
scala> eleventh((1 to 100).toStream.toIterator)
res1: Int = 11
scala> eleventh(Stream.from(1).toIterator)
res2: Int = 11
Looks fine. But then:
scala> eleventh((1 to 100).toIterator)
res3: Int = 1
Now (1 to 100).toIterator
has the same type as (1 to 100).toList.toIterator
, but the two behave very differently here—we're seeing implementation details leak out of the API. That's a very bad thing, and is the direct result of mixing purely functional combinators like take
with an inherently imperative and mutable concept like the iterator.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With