LoginSignup
16
11

More than 5 years have passed since last update.

SwiftのDictionaryに殺される

Last updated at Posted at 2015-11-23

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を代入した時挙動が違う

参考

Swiftにnilという値は存在しない - Qiita

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")]"

ここでハマるケースってあんまりイメージ出来ないけど、おなじに見えるコードが実は違うのに気づいて結構衝撃的だった。

16
11
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
11