Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

For-in loop goes one-too-far and finds 'nil' while unwrapping

It looks like the following Swift for-in loop is trying to go through more elements than are in the array.

For instance, this code:

var list:[Int?] = [1,2,3,4,5,6,7]

print("C-Style for loop")
for var i=0; i<list.count; i++ 
{
    var x:Int = list[i]!
    print(x)
}

print("\nFor-in loop")
for var x:Int! in list
{
    print(x)
}

Gets this output:

C-Style for loop
1
2
3
4
5
6
7

For each loop
1
2
3
4
5
6
7
fatal error: unexpectedly found nil while unwrapping an Optional value
... 
Illegal instruction: 4

I must be missing something here. Shouldn't list.count be the number of elements that the for-in loop tries to unwrap?

Not pictured above, the for-in loop works as expected if use an [Int] list instead of [Int?] with no unwrapping.

I am on Swift version 2.1.1.

like image 466
vgmoose Avatar asked Mar 27 '16 19:03

vgmoose


2 Answers

Basically you've done something you're not supposed to do, and thus you've stumbled on something that's probably a compiler bug (because the compiler didn't stop you). Very nice!

Now the solution. You are evidently trying to unwrap with your Int!. To unwrap safely, use for case syntax:

let list:[Int?] = [1,2,3,4,5,6,7]
for case let x? in list
{
    print(x) // 1, 2, 3, 4, 5, 6, 7
}

Another way (same result):

let list:[Int?] = [1,2,3,4,5,6,7]
list.flatMap{$0}.forEach{print($0)} // 1, 2, 3, 4, 5, 6, 7
like image 191
matt Avatar answered Sep 28 '22 04:09

matt


Forced Unwrapping like x:Int! is a dangerous coding style when you can't rule out nil. The same holds for var x:Int = list[i]!.

This works:

print("\nFor-in loop")
for x in list {
   print(x)
}

Here you allow x to be nil and can safely check for nil later.

Now to the question why your code breaks:

Internally, swift uses list.generate() for the for-in loop. Generators are nil terminated which is probably the reason why your code breaks.

like image 25
Gerd Castan Avatar answered Sep 28 '22 05:09

Gerd Castan