Cannot use optional chaining on non-optional value

I have a swift struct containing an array:


    struct S {
        let a: [Int]
    }


and an optional value of S


    var s: S? = ...


I thought I could use optional chaining for determining the size of the array a with a fallback of 0 like this


    let count = s?.a?.count ?? 0


However, the compiler complains with a


"Cannot use optional chaining on non-optional value of type [Int]"


I could add a computed property to S that delegates to a.count but I don't see why optional chaining with direct access to the array doesn't work. Is this a known issue? Will this be fixed?


(XCode 7.0 beta 4)

Accepted Reply

All the examples suggest to use ?. on every following property access.

I see. The part you referred is really misleading. But for this example snippet:

if let johnsStreet = john.residence?.address?.street {

This description is added:

The value of

john.residence
currently contains a valid
Residence
instance. However, the value of
john.residence.address
is currently
nil
.


The description suggests residence is Optional, address is also Optional. So, adding ? for each Optional property.


In general, expr?.methodChain is equivalent to (expr !=nil) ? expr!.methodChain : nil .

So, this code:

let count = s?.a.count ?? 0

is equivalent to:

let count = ( (s != nil) ? s!.a.count : nil ) ?? 0


And your code:

let count = (s?.a)?.count ?? 0

is equivalent:

let count = ( (s != nil) ? s!.a : nil )?.count ?? 0

When you terminate method chaining, the principle "If the type you are trying to retrieve is not optional, it will become optional because of the optional chaining." work. The result of s?.a may be nil, in other words, is Optional.


Your first code:

let count = s?.a?.count ?? 0

is equivalent to:

let count = ( (s != nil) ? s!.a?.count : nil) ?? 0

As you see s!.a is not Optional, it's non-optional [Int], so you get "Cannot use optional chaining on non-optional value of type [Int]" .


Actually I've forgotton how confusing these behaviors are, and I apologize I wrote "I don't see why you don't write".

But knowing a simple rule will always apply:

In a (continuous) method-chaining, you put `?` after properties or method calls which types are actually Optional.


(EDIT: added required spaces.)

Replies

I don't see why you don't write it as:

let count = s?.a.count ?? 0

You declare `a` as non-optional [Int], so you need not put `?` after `a`.

Ok thanks, this apparently works, but I'm still a little bit confused.

According to "Linking Multiple Levels of Chaining" (https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/OptionalChaining.html#//apple_ref/doc/uid/TP40014097-CH21-ID252) the a property will become optional:


"If the type you are trying to retrieve is not optional, it will become optional because of the optional chaining."

All the examples suggest to use ?. on every following property access.

It turns out that this works:


let aa: [Int]? = s?.a
let count = aa?.count ?? 0


and combining these two using parenthesis works as well


let count = (s?.a)?.count ?? 0


So what is the difference between the following two lines?


let c1: Int? = s?.a.count
let c2: Int? = (s?.a)?.count

All the examples suggest to use ?. on every following property access.

I see. The part you referred is really misleading. But for this example snippet:

if let johnsStreet = john.residence?.address?.street {

This description is added:

The value of

john.residence
currently contains a valid
Residence
instance. However, the value of
john.residence.address
is currently
nil
.


The description suggests residence is Optional, address is also Optional. So, adding ? for each Optional property.


In general, expr?.methodChain is equivalent to (expr !=nil) ? expr!.methodChain : nil .

So, this code:

let count = s?.a.count ?? 0

is equivalent to:

let count = ( (s != nil) ? s!.a.count : nil ) ?? 0


And your code:

let count = (s?.a)?.count ?? 0

is equivalent:

let count = ( (s != nil) ? s!.a : nil )?.count ?? 0

When you terminate method chaining, the principle "If the type you are trying to retrieve is not optional, it will become optional because of the optional chaining." work. The result of s?.a may be nil, in other words, is Optional.


Your first code:

let count = s?.a?.count ?? 0

is equivalent to:

let count = ( (s != nil) ? s!.a?.count : nil) ?? 0

As you see s!.a is not Optional, it's non-optional [Int], so you get "Cannot use optional chaining on non-optional value of type [Int]" .


Actually I've forgotton how confusing these behaviors are, and I apologize I wrote "I don't see why you don't write".

But knowing a simple rule will always apply:

In a (continuous) method-chaining, you put `?` after properties or method calls which types are actually Optional.


(EDIT: added required spaces.)

Thanks a lot for this detailed explanation.

In Groovy or the Spring expression language (SpEL) the so called "Save Navigation Operator" is left associative, i.e. a?.b?.c and (a?.b)?.c are equivalent. In Swift things are different as the methodChain in a?.methodChain is apparently right associative. Good to know.

I'm not sure if it is appropriate to use right associative, as `.` is not treated as a normal operator. But, if it helps you understand, I think we can say that method-chaining shows right-associative-like behavior.

And I should have known that other languages have similar thing as a normal left associative operator, thanks for enlighting me.