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,

Другие вопросы по тегам