是"bad form"在一条语句中执行映射查找和类型断言吗?

Is it "bad form" to perform map lookup and type assertion in one statement?

我刚刚意识到可以在一条语句中执行地图查找和 type/interface-assertion。

m := map[string]interface{}{
    "key": "the value",
}
if value, ok := m["key"].(string); ok {
    fmt.Printf("value exists and is a string: %s\n", value)
} else {
    fmt.Println("value does not exist or is not a string")
}

这算不算坏事? 我还没有看到任何官方文档对此进行评论。

编辑:我知道这段代码无法区分 "key doesn't exist" 和 "value is of incorrect type"。

edit2:咳咳,else 子句中的打印不正确:(

Is this considered bad?

不,这是完全错误的。您不能像这样组合地图查找和类型断言。

您的代码所做的是:

  1. 在地图无条件中查找"key"。如果 "key" 不在映射中,它将产生 nil(因为这是 interface{} 的零值)。

  2. 从地图中检索到的值(如果不在地图中则可能为 nil 类型断言为字符串。

您的代码无法确定 "key" 是否在地图中。

你所做的实际上是有效的,因为你使用了 type assertion 的特殊 comma-ok 习惯用法,如果断言不成立,它不会恐慌,并且因为映射可以用不在它(这将导致映射值类型的零值)。

的确,用这个你不能判断键是否在映射中,或者它是但它的值是 nil,但你已经怀疑这一点好像断言不成立, 你打印 "value does not exist or is not a string".

要测试所有 "corner" 个案例,请参阅此示例:

m := map[string]interface{}{
    "key":  "the value",
    "key2": 2,
    "key3": nil,
    // "key4":"", // Uncommented on purpose
}

for _, k := range []string{"key", "key2", "key3", "key4"} {
    if value, ok := m[k].(string); ok {
        fmt.Printf("[key: %s] value exists and is a string: %s\n", k, value)
    } else {
        fmt.Printf("[key: %s] value does not exist or is not a string: %s\n",
            k, value)
    }
}

输出(在 Go Playground 上尝试):

[key: key] value exists and is a string: the value
[key: key2] value does not exist or is not a string: 
[key: key3] value does not exist or is not a string: 
[key: key4] value does not exist or is not a string: 

所以基本上你可以使用它,它不会发生任何坏事(例如恐慌或内存泄漏),只需要知道它的限制(例如你无法获得关联的值对于 "key2" 因为它不是 string 类型)。

如果您的目的是获取键的值(如果它存在且类型为 string),那么这正是您的代码所做的。尽管您应该避免在需要的地方使用数据结构和构造,因为在大型项目中更难理解和维护。

我的意思是,例如,如果在您的代码中的某个时刻,您希望键 "somekey" 具有关联的 string 值,但它没有,您不会立即知道为什么会这样;是因为地图不包含该键,还是因为它包含但具有错误类型的值(或者该值甚至可能是 nil)?需要进一步测试/调试以追查根本原因。