Why does a let variable work in for ... in but not while let ... ?

I was trying out the use of Sequence and IteratorProtocol. But something here surprised me. There is no error on the for statement but the while statement has the error "Cannot use mutating member on immutable value: 'threeToGo' is a 'let' constant".

This looks like an inconsistency in Swift. They are both mutating the threeToGo variable. Can anyone give an explanation?

func even(_ i: Int) -> Bool { i % 2 == 0 }

struct Countdown: Sequence, IteratorProtocol {
    var count: Int
        
    mutating func next() -> Int? {
        if count == 0 {
            return nil
        } else {
            defer { count -= 1 }
            return count
        }
    }
}

let threeToGo = Countdown(count: 3)
while let i = threeToGo.next() {
    print(i)
    if even(i) { break }
}
print("We broke out")
for i in threeToGo {
    print(i)
}

Replies

Where does the for loop mutate threeToGo?

The for loop does not mutate threeToGo. It goes through each element in the sequence and prints the element.

The while loop generates a syntax error because you made the next function mutating. threeToGo is a let constant so you can't change it or call a mutating function on it.

  • You asked "Where does the for loop mutate threeToGo?" The for loop will have to call threeToGo.next(), which is a mutating func. func next() is the declaration required by IteratorProtocol to allow it to provide the values in a for loop.

Add a Comment

What @szymczyk explained, plus:

It is not a while vs for question.

Just try:

let newValue = threeToGo.next()

You will get the same error.

  • That is right. I was thinking the code in the for loop would also regard the expression after "for i in" as a let value.

  • The question is: Why does a let work in the for loop? It is still having to call next(). That is what makes it IteratorProtocol, and thus allowed to be used in a for loop.

Add a Comment