web-dev-qa-db-fra.com

Swift 3 interpolation de chaîne incorrecte avec des options implicitement déballées

Pourquoi options optionnelles implicitement dépliées n'est-elle pas dépliée lors de l'utilisation de l'interpolation de chaînes dans Swift 3?

Exemple: Exécution du code suivant dans la cour de récréation

var str: String!
str = "Hello"

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

produit cette sortie:

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

Bien sûr, je peux concaténer des chaînes avec le +, mais j'utilise l'interpolation de chaînes un peu partout dans mon application, ce qui ne fonctionne plus à cause de cela (bug?).

Est-ce même un bug ou ont-ils intentionnellement changé ce comportement avec Swift 3?

71
Keiwan

Selon SE-0054 , ImplicitlyUnwrappedOptional<T> N'est plus un type distinct; il n'y a que Optional<T> maintenant.

Les déclarations peuvent toujours être annotées en tant qu'options implicitement déballées T!, Mais cela ajoute simplement un attribut caché pour informer le compilateur que leur valeur peut être forcée de déballer dans des contextes qui demandent leur type déballé T ; leur type réel est maintenant T?.

Vous pouvez donc penser à cette déclaration:

var str: String!

comme ressemblant à ceci:

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

Seul le compilateur voit cet attribut @_implicitlyUnwrapped, Mais ce qu'il autorise est le déballage implicite de la valeur de str dans des contextes qui demandent un String (son type déballé):

// `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)

Mais dans tous les autres cas où str peut être vérifié en tant que fort optionnel, ce sera:

// `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.

Et le compilateur préférera toujours le traiter comme tel plutôt que de déplier la force.

Comme le dit la proposition (je souligne):

Si l'expression peut être explicitement vérifiée par un type fort facultatif, ce sera . Cependant, le vérificateur de type reviendra à forcer l'option si nécessaire. L'effet de ce comportement est que le résultat de toute expression faisant référence à une valeur déclarée comme T! Aura soit le type T soit le type T?.

En ce qui concerne l'interpolation de chaînes, sous le capot, le compilateur utilise cet initialiseur du protocole _ExpressibleByStringInterpolation afin d'évaluer un segment d'interpolation de chaînes:

/// 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)

Par conséquent, lorsqu'il est implicitement appelé par votre code:

var str: String!
str = "Hello"

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

Comme le type réel de str est String?, C'est par défaut ce que le compilateur déduira de l'espace réservé générique T. Par conséquent, la valeur de str ne sera pas forcée, et vous finirez par voir la description d'une option.

Si vous souhaitez qu'un IUO soit déplié de force lorsqu'il est utilisé dans l'interpolation de chaîne, vous pouvez simplement utiliser l'opérateur de débouclage forcé !:

var str: String!
str = "Hello"

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

ou vous pouvez contraindre à son type non facultatif (dans ce cas String) afin de forcer le compilateur à forcer implicitement le déballage pour vous:

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

les deux, bien sûr, planteront si str est nil.

71
Hamish