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?
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
(его развернутый тип):
// `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
// str is implicitly coerced from String? to Any
let y: Any = str
И компилятор всегда предпочитает рассматривать его как таковое по сравнению с принудительным развертыванием.
Как говорится в предложении (выделение мое):
Если выражение может быть явно проверено типом с сильным необязательным типом, это будет. Тем не менее, проверка типов вернется к принудительному необязательному при необходимости. Эффект такого поведения заключается в том, что результатом любого выражения, которое ссылается на значение, объявленное как
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
,