1

In Go, let m is map object that map string to int, suppose "foo" is not a key of m, then the statement

m["foo"]

returns two value 0 and false which false implies "foo" is not a key of m. Could you give some situations, examples that the go's property is useful?

ydhhat
  • 381
  • 2
  • 15
  • 2
    That depends on what you mean by "raise an error"; in Go, errors are return values. In the case of a map the only possible "error" is that the requested key doesn't exist, so it might as well just be a boolean. However, to know the true answer behind any design decision, you'd need to ask the designer, not the community at large. – Adrian Jan 28 '19 at 17:16
  • Why someone downvote my question? Tell me the reasons so I can ask better questions. – ydhhat Jan 28 '19 at 17:29
  • 1
    I downvoted for the normal reason: "The question does not show any research effort; it unclear or not useful." Most significantly, it is unclear. – Jonathan Hall Jan 28 '19 at 18:01
  • @Flimzy, Could you give more details about unclear part? Is that about the question "what is benefit of doing that?"? And a clearer question should be "Could you give some situations, examples that the go's property is useful?"? – ydhhat Jan 28 '19 at 18:27
  • @ydhhat: go doesn't "raise errors", error are just values. if you want to treat the `bool` value as an error you can. How would your program be any different if the second value was an `error`, besides being slightly more verbose? – JimB Jan 28 '19 at 18:31
  • «Could you give some situations, examples that the go's property is useful?» A maps can be thought of as keeping a related set of named _variables._ It's useful to have these variables obey the general Go rules of having all variables implicitly initialized to the so-called "zero value" appropriate for their type—unless explicitly initialized. This means that if a map does not have a variable bound to a key "foo", reading its value using that key yields the zero value of the appropriate type. You may then update that value and write it back. – kostix Jan 28 '19 at 18:46
  • 1
    Consider that `m := make(map[string]int); x := m["foo"]; m["foo"] = x + 1` is not really different from a supposedly more familiar `var v int; x := v; v = x + 1`. In both the examples, the second step acquires the zero value of `int` adds one to it and then and assigns it back to the variable used to read the zero value from in the first step. In the case of a map, that variable is first created. – kostix Jan 28 '19 at 18:48
  • 2
    Also do not forget that map access in Go may use two argument form: `v, ok := map["key"]`, so you can use `if v, ok := map["key"]; !ok { panic("boom") } ...` if you want. It's not more complicated than having maps panic on non-existing keys with the programmer having to trap them if they want different behaviour. – kostix Jan 28 '19 at 18:50
  • @JimB, saying "go doesn't "raise errors"" you mean go not handle errors as exceptions like in this article https://medium.com/@hussachai/error-handling-in-go-a-quick-opinionated-guide-9199dd7c7f76 – ydhhat Jan 28 '19 at 18:52
  • @ydhhat: I mean that errors are just values, like any other, and you can choose to deal with them however you need. There is nothing special about error values, other than they indicate that they are important to check. – JimB Jan 28 '19 at 19:07
  • 1
    It's unclear because you're using terminology that makes no sense in the context of Go. Go never "raises errors"--it never raises _anything_, for that matter. So it's unclear what you mean, or which behavior you're expecting. – Jonathan Hall Jan 28 '19 at 19:09
  • 1
    Would you rather go _"raises an error"_ (which to me implies a runtime panic) when all you want to do is check whether or not a given key set in a map? Jesus.. that would make for some verbose, ugly code – Elias Van Ootegem Jan 28 '19 at 19:52
  • I remove the "raise an error" part in my question. – ydhhat Jan 28 '19 at 20:28
  • @ydhhat: No, your title still asks about raising errors. – Jonathan Hall Feb 04 '19 at 10:13
  • @Flimzy I change `raising error` to `raising error (like Python)` in the title. – ydhhat May 10 '19 at 13:52
  • It's a perfectly good question, asked in good faith -- with some helpful answers as well. – Brent Bradburn May 23 '20 at 23:07
  • *May* be OK to close this if you dup-link to [How to check if a map contains a key in Go?](https://stackoverflow.com/q/2050391/86967) -- although this question provides an opportunity to go a little deeper into Go design principles. – Brent Bradburn May 23 '20 at 23:10

2 Answers2

5

There was a time before Go 1 when indexing a map with a non-existing key crashed the app. This was changed to return the zero value of the value type of the map. This was a design decision which allows the (limited) use of uninitialized language constructs to be used without extra checks, which simplifies code.

For example you can for range over nil slices and nil maps, you can check their length etc. The result is the same of course: iterating over a nil slice or map will result in zero iterations, and the length of nil slices and maps is 0, but you do not need to use an if statement beforehand to tell if the value is non-nil (in order to tell if you can range over them or index them).

PeterSO already showed a good example when the zero value for non-existing keys is useful: counters.

Another famous example is a map being used as a set. If you choose the value type to be bool, you don't have to initialize values that are not in the set. Indexing the map tells if a value (the key) is in the set, and if it's not, the zero value of bool type being false will tell you that it's not in the set.

For example:

fruits := map[string]bool{}

// Add elements to the set:
fruits["apple"] = true
fruits["banana"] = true

// Test if elements are in the map:
fmt.Println("Is apple in the set?", fruits["apple"])
fmt.Println("Is banana in the set?", fruits["banana"])
fmt.Println("Is plum in the set?", fruits["plum"])
fmt.Println("Is lemon in the set?", fruits["lemon"])

Output (try it on the Go Playground):

Is apple in the set? true
Is banana in the set? true
Is plum in the set? false
Is lemon in the set? false
icza
  • 389,944
  • 63
  • 907
  • 827
3

Go does not raise errors. Go reports errors.


A common use of a map element zero value:

package main

import (
    "fmt"
)

func main() {
    counts := map[string]int{}
    fmt.Println(counts)

    // Equivalent:
    // counts["foo"] = counts["foo"] + 1
    // counts["foo"] += 1
    // counts["foo"]++

    counts["foo"]++
    fmt.Println(counts)

    counts["foo"]++
    fmt.Println(counts)
}

Playground: https://play.golang.org/p/nvHBrgV_lFU

Output:

map[]
map[foo:1]
map[foo:2]

A common use of the comma, ok form map index expression:

package main

import (
    "fmt"
)

func main() {
    m := map[string]map[string]int{}
    fmt.Println(m)

    // panic: assignment to entry in nil map
    // m["K1"]["K2"]++

    _, ok := m["K1"]
    if !ok {
        m["K1"] = map[string]int{}
    }
    m["K1"]["K2"]++
    fmt.Println(m)

    m["K1"]["K2"]++
    fmt.Println(m)
}

Playground: https://play.golang.org/p/9byxSSIWBre

Output:

map[]
map[K1:map[K2:1]]
map[K1:map[K2:2]]
peterSO
  • 158,998
  • 31
  • 281
  • 276