Confused Confused - 1 month ago 16
Swift Question

Accessing indices of Swift arrays safely: Ext return not an optional?

In the docs, there's an example of an extension to add safe and unknowing access to any index (real or imagined) of a Swift array, here.

Swift arrays normally crash if the index doesn't exist. It seems this extension is explicitly designed to solve this problem by returning

nil
if there's nothing at the index requested.

The extension looks like this:

extension Array {
subscript (safe index: Int) -> Element? {
return indices ~= index ? self[index] : nil
}
}


Use of the extension looks like this:

if let thirdValue = array[safe: 2] {
print(thirdValue)
}


Trying this out in playgrounds reveals nothing coming back for an unsafe index. That I can tell. Not an optional, not a nil, nothing. And no crash.

How is this not an optional that's coming back from this when it's a safe or unsafe index value? I admire that this works, just don't really understand how it works, or what it's doing.

UPDATE



As Martin R helpfully points out, most of my problem is not understanding/remembering/knowing that
if let
is doing a forceful unwrapping of the optional that results from the extension's function returning a value that is most definitely an Optional. If this forceful unwrapping fails to divulge a value (ie no value at the index returns an optional containing
nil
) this prevents the
{trailing closure}
from being invoked.

However it brings up another subquestion.

If using a variable to capture the return value, like so:

let val = array[safe: 2]
print("val:", val)


regardless of the result, it is an optional, but Swift provides a "helpful" error saying:


Expression implicitly coerced from 'Int?' to Any


I apologise if this should be a new question: Why is this being coerced to
Any
? Why can't (and why doesn't it) simply remain as an Optional of type
Int?

Answer
if let val = someOptional { ... }

is an optional binding. If someOptional != nil then the condition is true and the unwrapped value someOptional! is assigned to val. If someOptional == nil then the condition is false.

So in your case

if let thirdValue = array[safe: 2] {
    print(thirdValue)
}

the condition is true (and the if-block executed) if array[safe: 2] != nil. In that case, the unwrapped value is assigned to thirdValue. On the other hand, if array[safe: 2] returns nil then the condition is false and the if-block is not executed.

You can print the return value without using an if-condition to verify that it is nil:

let array = [1, 2]
let val = array[safe: 2]
print("val:",  val) // "val: nil"

With Swift 3.0.1 (Xcode 8.1, tested with GM seed) this will cause compiler warnings

main.swift:12:16: warning: expression implicitly coerced from 'Int?' to Any
print("val:",  val)
               ^~~
main.swift:12:16: note: provide a default value to avoid this warning
print("val:",  val)
               ^~~
                   ?? 
main.swift:12:16: note: force-unwrap the value to avoid this warning
print("val:",  val)
               ^~~
                  !
main.swift:12:16: note: explicitly cast to Any with 'as Any' to silence this warning
print("val:",  val)
               ^~~
                   as Any

as a consequence of the implementation of SE-0140 Warn when Optional converts to Any, and bridge Optional As Its Payload Or NSNull.

The warning can be suppressed with

print("val:",  val as Any) // "val: nil"