Подтвердить что ты не робот

Swift 3 некорректная строковая интерполяция с неявно развернутыми опциями

Почему неявно развернутые опции не распаковываются при использовании строковой интерполяции в Swift 3?

Пример: Выполнение следующего кода на игровой площадке

var str: String!
str = "Hello"

print("The following should not be printed as an optional: \(str)")

производит этот вывод:

The following should not be printed as an optional: Optional("Hello")

Конечно, я могу конкатенировать строки с помощью оператора +, но я использую строчную интерполяцию почти везде в моем приложении, которая теперь больше не работает из-за этого (ошибка?).

Является ли это даже ошибкой или они намеренно изменяют это поведение с помощью Swift 3?

4b9b3361

Ответ 1

Согласно SE-0054, ImplicitlyUnwrappedOptional<T> больше не является отдельным типом; теперь есть только Optional<T>.

Объявления по-прежнему могут быть аннотированы как неявно развернутые необязательные T! , но при этом просто добавляется скрытый атрибут, информирующий компилятор о том, что их значение может быть принудительно развернуто в контекстах, которые требуют их развернутого типа T; их фактический тип теперь T? ,

Итак, вы можете подумать об этой декларации:

var str: String!

как на самом деле выглядит так:

@_implicitlyUnwrapped // this attribute name is fictitious 
var str: String?

Только компилятор видит этот атрибут @_implicitlyUnwrapped, но он допускает неявное развертывание значения str в контекстах, которые требуют String (его тип unwrapped):

// 'str' cannot be type-checked as a strong optional, so the compiler will
// implicitly force unwrap it (causing a crash in this case)
let x: String = str

// We're accessing a member on the unwrapped type of 'str', so it'll also be
// implicitly force unwrapped here
print(str.count)

Но во всех других случаях, когда str может быть проверен на тип как сильная опция, это будет:

// 'x' is inferred to be a 'String?' (because we really are assigning a 'String?')
let x = str 

let y: Any = str // 'str' is implicitly coerced from 'String?' to 'Any'

print(str) // Same as the previous example, as 'print' takes an 'Any' parameter.

И компилятор всегда предпочитает рассматривать его как таковое по сравнению с принудительным развертыванием.

Как говорится в предложении (выделение мое):

Если выражение может быть явно проверено типом с сильным необязательным типом, это будет. Тем не менее, проверка типов вернется к принудительному необязательному при необходимости. Эффект такого поведения заключается в том, что результатом любого выражения, которое ссылается на значение, объявленное как T! будет иметь тип T или тип T? ,

Когда дело доходит до интерполяции строк, компилятор использует этот инициализатор из протокола _ExpressibleByStringInterpolation для оценки сегмента интерполяции строки:

/// Creates an instance containing the appropriate representation for the
/// given value.
///
/// Do not call this initializer directly. It is used by the compiler for
/// each string interpolation segment when you use string interpolation. For
/// example:
///
///     let s = "\(5) x \(2) = \(5 * 2)"
///     print(s)
///     // Prints "5 x 2 = 10"
///
/// This initializer is called five times when processing the string literal
/// in the example above; once each for the following: the integer '5', the
/// string '" x "', the integer '2', the string '" = "', and the result of
/// the expression '5 * 2'.
///
/// - Parameter expr: The expression to represent.
init<T>(stringInterpolationSegment expr: T)

Поэтому, когда неявно вызывается вашим кодом:

var str: String!
str = "Hello"

print("The following should not be printed as an optional: \(str)")

Как str фактическим типом является String? по умолчанию это то, что компилятор выведет из общего заполнителя T Поэтому значение str не будет принудительно развернуто, и вы в конечном итоге увидите описание для необязательного.

Если вы хотите, чтобы IUO был принудительно развернут при использовании в интерполяции строк, вы можете просто использовать оператор принудительного развертывания ! :

var str: String!
str = "Hello"

print("The following should not be printed as an optional: \(str!)")

или вы можете привести его к необязательному типу (в данном случае String), чтобы принудительно заставить компилятор принудительно развернуть его для вас:

print("The following should not be printed as an optional: \(str as String)")

оба из которых, конечно, потерпят крах, если str равен nil.