2016-08-05 4 views
7

Tout d'abord, je tente de mapper un [String?], pour obtenir un [String]:Pourquoi Swift nil-coalescing retourne-t-il un optionnel?

$ xcrun swift 
Welcome to Apple Swift version 2.2 (swiftlang-703.0.18.8 clang-703.0.30). Type :help for assistance. 
    1> import Foundation 
    2> let j: [String?] = ["a", nil] 
j: [String?] = 2 values { 
    [0] = "a" 
    [1] = nil 
} 
    3> j.map {$0 ?? ""} 
$R0: [String] = 2 values { 
    [0] = "a" 
    [1] = "" 
} 

Cela fait sens pour moi. Je ne fusionne aucun String? et j'obtiens un String. Mais avec [AnyObject?], quelque chose d'étrange se produit:

4> let k: [AnyObject?] = ["a", nil] 
k: [AnyObject?] = 2 values { 
    [0] = "a" 
    [1] = nil 
} 
    5> k.map {$0 ?? ""} 
$R1: [AnyObject?] = 2 values { 
    [0] = "a" 
    [1] = (instance_type = 0x00007fff7bc2c140 @"") 
} 

Je suis Optionals nul coalescent, mais cette fois je sors une option. Pourquoi?

Le Swift Programming Language dit a ?? b est un raccourci pour a != nil ? a! : b, mais lorsque je tente, je sortir un tableau de non-optionals:

6> k.map {$0 != nil ? $0! : ""} 
$R2: [AnyObject] = 2 values { 
    [0] = "a" 
    [1] = "" 
} 

que je comprends mal comment ?? est censé fonctionner? Qu'est-ce qui se passe ici?

+2

question intéressante, pour cette raison cela semble fonctionner 'res laisser: [ANYOBJECT] = k.map {$ 0 ?? ""} ' –

+2

On dirait un problème d'inférence de type bizarre avec' AnyObject' et littéraux - cela fonctionne aussi: 'laisser k1 = k.map {$ 0 ?? String()} ' – Hamish

Répondre

0

Il est venu à mon attention que Apple a examiné ce un bogue dans Swift 2.

Swift 3, le 1er exemple ci-dessus fonctionne toujours, tandis que les 2e et 3e exemples sont une syntaxe incorrecte (avec ou sans pontage Fondation).

Remplacement de la déclaration AnyObject avec Any œuvres: a ?? b se comporte alors identique à a != nil ? a! : b, comme dit la documentation.

2

Le comportement détaillé est pas bien documenté, donc, changerait à l'avenir Martinets.

Mais vous devez savoir opérateur coalescent a deux: les surcharges

@warn_unused_result 
public func ??<T>(optional: T?, @autoclosure defaultValue:() throws -> T) rethrows -> T 

@warn_unused_result 
public func ??<T>(optional: T?, @autoclosure defaultValue:() throws -> T?) rethrows -> T? 

Dans votre cas, Swift a choisi ce dernier pour votre code.

Vous pouvez tester avec un code simplifié comme:

let x: AnyObject? = "a" 
x ?? "" 

Le type inféré (à Swift 2.2.1) devient AnyObject?. Mais ce code est également valide.

let y: AnyObject = x ?? "" 

littéraux de chaîne comme "" peuvent être traitées comme variété de types. Tout cela est valable dans Swift.

"" as String 
"" as String? 
"" as NSString 
"" as NSString? 
"" as AnyObject 
"" as AnyObject? 

Ainsi, avec une raison inconnue, Swift a choisi AnyObject?. Et, en cas inférence de type peut être ambigu, vous devez utiliser l'annotation de type explicite, comme suggéré dans le commentaire de appzYourLife.

+0

Intéressant! Il semble donc que la coalescence nulle est définie * dans * Swift, c'est-à-dire que ce n'est pas seulement une syntaxe spéciale. Mais l'opérateur ternaire est une syntaxe spéciale, n'est-ce pas? Cela suggère-t-il que les règles d'inférence de type pour les surcharges d'opérateurs sont (peut-être involontairement) différentes de celles des opérateurs intégrés? –

+0

Peut-être que je peux dire oui pour les deux questions. Vous ne pouvez écrire aucun opérateur ternaire dans Swift, même si vous essayez de surcharger '?:'. Et à propos des règles d'inférence de type, le fait est comme vous le voyez. Et je ne dis pas que ce fait est ce qu'il devrait être. Compte tenu des cas d'utilisation de l'opérateur de coalescence nul, Swift devrait utiliser la première stratégie non-optionnelle, je pense. – OOPer