12

I ask since I like that maps do not allow multiple keys. I know you can do something like the below where your values are bools or empty struct, but is there a way to get around specifying any value for your keys? Is there some advantage to having to specify an empty struct?

Related question, but focused on appending only unique values.

type N struct {}

func tengoQueCagar() {
    var map_almost_empty_value1 = map[int]bool{0:true,1:false}
    var map_almost_empty_value2 = map[int]struct{}{0:struct{}{},1:struct{}{}} //long and seems like lame syntax...
    var map_almost_empty_value3 = map[int]N{0:N{},1:N{}} //shorter.. better?

    var map_not_possible_empty_value_2 = map[int]nil{0:nil,1:nil} // better than empty struct syntax... but not possible
    var map_not_possible_empty_value_2 = map[int]{0,1} // ideally possible... but not... 

    //do something...
}
John Drinane
  • 1,279
  • 2
  • 14
  • 25

2 Answers2

19

struct{} requires 0 bytes to store. If you declare a map with struct{} values, you'd only be storing map keys.

There's a nice blog post about it: https://dave.cheney.net/2014/03/25/the-empty-struct

If you want to use a map like a set, it might help to declare a separate type for it:

type IntSet map[int]struct{}

And you can add some convenience methods to it, such as:

func (i IntSet) Has(v int) bool {
  _, ok := i[v]
  return ok
}
Burak Serdar
  • 46,455
  • 3
  • 40
  • 59
12

For a direct answer to your question: See below!

This "empty struct{} as map values trick" comes up often and the argument in favor is always of the form "so the values do not need storage space".

The other natural thing to do is use a map[int]bool.

If memory is really a constraint of your application (e.g. because you are storing millions or billions of keys in your map) then yes: use struct{}. For all other, normal cases: Using struct{} makes such map literals awkward and key lookup more complicated as you have to use the comma-ok-variant (if _,ok := m[k]; ok {.

For bool values you can do a simple m[k] which is less to type and is easier to understand.

Personally I think use of struct{} as map value is a unnecessary, premature, overly clever optimisation. If you really need to save these few bytes because your map will contain millions of entries then probably a map is not the right data structure anyway: Depending on the use case bit vectors, sparse data structures or even probabilistic data structures (bloom-, cuckoo-filters), union-find, etc. might be much better suited.

Answer: No. In a map literal you have to specify each and every key. (General rule of thumb: There is no syntactic sugar in Go; there are no clever shortcuts in Go; everything is explicit in Go.)

Volker
  • 40,468
  • 7
  • 81
  • 87
  • 1
    One downside of using `map[K]bool` for a set is miss-use where a `false` value is stored into the map. I've seen such maps used as sets where someone adding/editing the code did `m[someKey] = false` instead of `delete(m, someKey)` which of course breaks any code using `if m[someKey]` as a set membership test. – Dave C Aug 23 '19 at 13:11
  • 1
    @DaveC "which of course breaks any code using if m[someKey] as a set membership test." Why? m[somekey] == false in this case. – Volker Aug 23 '19 at 19:24
  • 1
    D'oh, I got it backwards, the `if m[someKey]` test keeps working but anything doing a `if _, ok := m[someKey]; ok` says it exists but someone doing a `m[someKey] = false` probably intended the key to not exist. – Dave C Aug 24 '19 at 10:34
  • 1
    Yet, using bool value is the recommended way for using map as a set: https://blog.golang.org/maps#TOC_4. – Alex Jun 18 '21 at 12:14