Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

I just realized that it's possible to do a map lookup and a type/interface-assertion in one statement.

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

Is this considered bad? I've not seen any official documentation commenting this.

edit: I know this code can not distinguish between "key doesn't exist" and "value is of incorrect type".

edit2: ahem, the print in the else-clause was incorrect :(

like image 740
pythonator Avatar asked Sep 19 '17 12:09

pythonator


1 Answers

What you do actually works, because you use the special comma-ok idiom of the type assertion which does not panic if the assertion does not hold, and because maps can be indexed with keys which are not in it (which will result in the zero value of the value type of the map).

It's true that with this you can't tell if the key is in the map or not, or it is but its value is nil, but you already suspected this as if the assertion does not hold, you print "value does not exist or is not a string".

To test all "corner" cases, see this example:

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)
    }
}

Output (try it on the 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: 

So basically you can use this, nothing bad will happen from it (e.g. panic or memory leak), just know its limits (e.g. you couldn't get the value associated for "key2" as it's not of string type).

If your intention is to get the value for a key if it exists and is of type string, then this is what your code exactly does. Although you should avoid data structures and constructs where you need this, as it's harder to understand and maintain in larger projects.

What I mean by this is for example if at some point in your code you expect the key "somekey" to have a string value associated and it does not, you won't immediately know why it is so; is it because the map does not contain that key, or is it because it does but with a value of wrong type (or the value may even be nil)? Further testing / debugging is needed to track down the root cause.

like image 105
icza Avatar answered Oct 12 '22 09:10

icza