HalR
HalR

Reputation: 11083

Surprise results iterating over an array with an optional in Swift

I was surprised that this swift code behaves nicely:

let values = ["Hello", "Test"]

var count = 0
for string: String in values {
    count = count + 1
    print("count is: ", count)
    print(string)
} 

with output of:

count is:  1
Hello
count is:  2
Test

but making the String into String? creates an infinite loop.

   let values = ["Hello", "Test"]

    var count = 0
    for string: String? in values {
        count = count + 1
        print("count is: ", count)
        print(string)
    }

with output of:

count is:  1
Optional("Hello")
count is:  2
Optional("Test")
count is:  3
nil
count is:  4
nil
count is:  5
nil
count is:  6
nil
count is:  7
nil
count is:  8
(ad infinitum)

Swift has been so good at catching weird code problems that I was surprised I could walk into such a mess without warning or error. Is this really what one would expect from Swift 4? And if so, why?

Upvotes: 5

Views: 324

Answers (3)

Martin R
Martin R

Reputation: 539945

To understand this issue it helps to recollect how for-in loops work:

for s in values {
    print(s)
}

creates an iterator of the sequence, and calls the iterator's next() method until that returns nil:

var it = values.makeIterator()
while let s = it.next() {
    print(s)
}

Your second version is equivalent to

var it = values.makeIterator()
while let s: String? = it.next() {
    print(s)
}

and now the compiler warns:

warning: explicitly specified type 'String?' adds an additional level
of optional to the initializer, making the optional check always succeed
    while let s: String? = it.next() {
          ^      ~~~~~~~   ~~~~~~~~~
                 String

So what happens here is that the String? returned from it.next() is wrapped into a “nested optional” .some(it.next()) of type String??, which is then optionally bound to s: String?. This does always succeed, because .some(it.next()) is not String??.none. Therefore the loop never terminates.

One could argue that the compiler should warn as well for

for s: String? in values {
    print(s)
}

Upvotes: 8

Daniel Larsson
Daniel Larsson

Reputation: 527

There is no error. In the first function it will keep going until it no more strings.

But other hand the second function you set the string to optional so when its no more string it will still keep going. Because nil is a nil value and it’s not the same as nothing. You have nothing that ends that loop.

Upvotes: 0

O. Shulzhenko
O. Shulzhenko

Reputation: 35

Your "for" runs on indexes. Under the index in excess of the number of elements is nill.

Upvotes: 0

Related Questions