Свифт 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 60

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 был принудительно развернут при использовании в интерполяции строк, вы можете просто использовать оператор force unwrap !:

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 is nil.