Безопасный доступ к индексам массивов Swift: возврат Ext не является необязательным?

В документах есть пример расширения для добавления безопасного и неизвестного доступа к любому индексу (реальному или воображаемому) массива Swift (Edit - SO Docs больше не существует, поэтому этот пример был вставлен внизу этого вопроса ).

Массивы Swift обычно аварийно завершают работу, если индекс не существует. Похоже, это расширение специально предназначено для решения этой проблемы, возвращая nil, если в запрошенном индексе ничего нет.

Расширение выглядит так:

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

Использование расширения выглядит следующим образом:

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

Попытка этого на игровых площадках не показывает ничего возвращающегося для небезопасного индекса. Это я могу сказать. Ни факультатив, ни ноль, ничего. И никакого краха.

Как это не является необязательным, что возвращается из этого, когда это безопасное или небезопасное значение индекса? Я восхищаюсь тем, что это работает, просто не очень понимаю, как это работает или что оно делает.

ОБНОВИТЬ

Как услужливо отмечает Мартин Р, большая часть моей проблемы заключается в том, чтобы не понимать/не помнить/знать, что if let совершает принудительное развертывание необязательного параметра, который является результатом функции расширения, возвращающей значение, которое определенно является необязательным. Если эта принудительная распаковка не раскрывает значение (т. е. ни одно значение в индексе не возвращает необязательное значение, содержащее nil), это предотвращает вызов {trailing closure}.

Однако возникает еще один подвопрос.

Если вы используете переменную для захвата возвращаемого значения, например:

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

независимо от результата, это необязательно, но Swift выдает «полезную» ошибку:

Выражение, неявно полученное из 'Int?' любому

Прошу прощения, если это должен быть новый вопрос: почему это принуждают к Any? Почему нельзя (и почему бы и нет) просто оставаться необязательным типом Int?


Следующее содержимое взято из «Безопасный доступ к индексам» из документации по переполнению стека (заархивировано здесь); авторские права 2017 г. принадлежат Moriya; под лицензией CC BY-SA 3.0. Архив полного содержимого документации Stack Overflow можно найти на сайте archive.org, где этот пример проиндексирован по идентификатору темы: 284, например: 16567.

Добавляя следующее расширение к индексам массива, можно получить доступ, не зная, находится ли индекс внутри границ.

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

пример:

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

person Confused    schedule 26.10.2016    source источник


Ответы (1)


if let val = someOptional { ... }

является необязательной привязкой. Если someOptional != nil, то условие истинно и развернутое значение someOptional! присваивается val. Если someOptional == nil, то условие ложно.

Итак, в вашем случае

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

условие истинно (и блок if выполняется), если array[safe: 2] != nil. В этом случае развернутое значение присваивается thirdValue. С другой стороны, если array[safe: 2] возвращает nil, то условие ложно и блок if не выполняется.

Вы можете напечатать возвращаемое значение без использования условия if, чтобы убедиться, что оно равно nil:

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

С Swift 3.0.1 (Xcode 8.1, протестировано с GM seed) это вызовет предупреждения компилятора

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

как следствие реализации SE-0140 Предупреждать, когда необязательный преобразуется в любой, и связывать необязательный как его полезную нагрузку или NSNull.

Предупреждение можно отключить с помощью

print("val:",  val as Any) // "val: nil"
person Martin R    schedule 26.10.2016
comment
аааа, если пусть меня поймает, СНОВА!!! Я был бы миллионером, если бы получал пенни каждый раз, когда эта, казалось бы, безобидная маленькая ловушка/трюк застает меня врасплох. - person Confused; 26.10.2016
comment
Тем не менее, я получаю (на игровых площадках) необязательные значения в качестве результатов. Это просто для того, чтобы заморочить мне голову? Чтобы уточнить, результатом может быть либо отдельное (необязательное) значение, либо ноль. Необязательное возвращение из этого, никогда. Возможно, я не понимаю вашего ответа. Мне кажется, что вы говорите, что необязательное значение возвращается, но вы начинаете с If..., так что я не уверен. - person Confused; 26.10.2016
comment
Дерьмо. Я до сих пор не гроккаю, если позволите. это принудительное преобразование в необязательный, верно? - person Confused; 26.10.2016
comment
Я переписал ответ - так понятнее? Предупреждающее выражение, неявно полученное из 'Int?' to Any был представлен в Swift 3.1, см. noreferrer">github.com/apple/swift-evolution/blob/master/proposals/. - person Martin R; 26.10.2016
comment
Как раз тогда, когда я подумал, что опции больше не могут быть запутанными... ФАРК!!! - person Confused; 26.10.2016
comment
После прочтения вашей ссылки на эволюцию этого ... Означает ли это, что print(), реализованный Apple, выполняется в Objective-C? Я не понимаю, как эта ерунда as Any должна появляться из вызова Swift, если это предупреждение о переходе к преобразованиям Options в Any в Objective-C. Но я в замешательстве... - person Confused; 26.10.2016