Objc時代からある「Dictionaryにnilを代入すると、その要素が削除される」というふるまいがSwiftのOptionalと合わさるとだいぶ直感的でない気がする。
nil代入で要素が削除される
これはまあ、わかる(そういうもの、という認識)
// nil代入で配列から要素を削除する
var dict: [String: String] = [
"apple": "red",
"banana": "yellow",
"peach": "pink",
]
print(dict) // -> "[banana: yellow, apple: red, peach: pink]"
dict["banana"] = nil
print(dict) // -> "[apple: red, peach: pink]"
非Optionalな型をもつDictionaryの定義時にOptional型の変数を入れられない
これもまあ、わかる(型違うし)
var firstName: String = "Steven"
var middleName: String? = "Paul" // <- middleNameはoptional
var lastName: String = "Jobs"
var dict: [String: String] = [ // -> '[String: String?]' is not convertible to '[String: String]'
"firstName": firstName,
"middleName": middleName,
"lastName": lastName,
]
定義時には入れられないけど、後からなら入れられる
"nil代入で削除されるので正しい"と"非Optionalな型にOptionalな値を入れようとするとコンパイルエラー"がぶつかってて腑に落ちない。
var firstName: String = "Steven"
var middleName: String? = "Paul" // <- middleNameはoptional
var lastName: String = "Jobs"
var dict: [String: String] = [
"firstName": firstName,
"middleName": "",
"lastName": lastName,
]
dict["middleName"] = middleName // -> ちなみにmiddleNameがnilだった時はdictからmiddleName要素が消える
print(dict) // -> "[middleName: Paul, firstName: Steven, lastName: Jobs]"
要素の型がOptionalな時、nilを代入した時とOptional.Noneを代入した時挙動が違う
参考
var firstName: String = "Steven"
var middleName: String? = nil // <- middleNameだけoptional
var lastName: String = "Jobs"
var dict: [String: String?] = [
"firstName": firstName,
"middleName": "Some name", // <- なんか入ってる、くらいの意味
"lastName": lastName,
]
dict["middleName"] = middleName // <- nilが代入されるように見える = 要素が消えるのでは?
print(dict) // -> "[lastName: Optional("Jobs"), firstName: Optional("Steven"), middleName: nil]" -> middleName消えてないやんけ、nil入っとるやんけ
dict["middleName"] = middleName
ここで代入されるのは、nilではなくてString?.Noneである。ので、String?型を要素に持つdictはString?.Nomeを値として握る。要素は削除されない。
Optional bindingの知識だけだと以下の2つのコードは同じものに見えるが実際は違う。
var firstName: String = "Steven"
var middleName: String? = nil // <- middleNameだけoptional
var lastName: String = "Jobs"
var dict: [String: String?] = [
"firstName": firstName,
"middleName": "Some name",
"lastName": lastName,
]
dict["middleName"] = middleName // <- ここ
print(dict) // -> "[lastName: Optional("Jobs"), firstName: Optional("Steven"), middleName: nil]"
var firstName: String = "Steven"
var middleName: String? = nil // <- middleNameだけoptional
var lastName: String = "Jobs"
var dict: [String: String?] = [
"firstName": firstName,
"middleName": "Some name",
"lastName": lastName,
]
if let middleName = middleName { //
dict["middleName"] = middleName //
} else { // <- ここ
dict["middleName"] = nil //
} //
print(dict) // -> "[firstName: Optional("Steven"), lastName: Optional("Jobs")]"
ここでハマるケースってあんまりイメージ出来ないけど、おなじに見えるコードが実は違うのに気づいて結構衝撃的だった。