Go 的 map 讀取不存在的 key 會回傳 zero value

讀一個不存在的 key 不會報錯、也不會回傳 nil,而是回傳 value 型別的 zero value。所以 m["nobody"] 得到 0 時,你分不出是「沒有這個人」還是「這個人的值就是 0」。

scores := map[string]int{"Amy": 0}
 
scores["Amy"]      // 0  ← 有這個 key,值就是 0
scores["Nobody"]   // 0  ← 沒有這個 key

comma ok:多接一個 bool 來分辨

v, ok := scores["Nobody"]
//  ↑        ↑
//  值        這個 key 存在嗎

前面是 value,後面是 bool——兩個不同的東西,別接反:

v, ok := scores["Amy"]      // v=0,  ok=true    ← key 在,值真的是 0
v, ok := scores["Nobody"]   // v=0,  ok=false   ← key 不在

兩次 v 都是 0,靠 ok 才分得出差別。慣用寫法是塞進 if 的初始化陳述句裡:

if v, ok := scores["Amy"]; ok {
    fmt.Println(v)   // 只有 key 真的存在才進來
}

ok 的型別永遠是 bool,跟 map 的 value 型別無關:

names := map[int]string{}
s, ok := names[1]   // s = ""(string 的 zero value),ok = false

這不是函式回傳,是語言內建的語法

map 索引式在「接兩個變數」的位置時,編譯器會多給一個 bool。同一個運算式接一個或接兩個都合法:

v := scores["Amy"]       // 接一個:只給值
v, ok := scores["Amy"]   // 接兩個:值 + 存在與否

同樣的 comma ok 形式在 channel 接收(v, ok := <-ch)與 type assertion(v, ok := x.(int))也有,是語言層級的慣例而不是 map 專屬。

if ok 之所以能寫,正是因為它本來就是 bool——Go 沒有 truthy/falsy,如果 ok 接到的是 value,if ok 根本編譯不過。

「讀取永遠有答案、不需要碰到任何記憶體」也是為什麼 nil map 可以讀

相關:Go 建立 map 的三種寫法