203

I am trying to declare to constant in Go, but it is throwing an error.

This is my code:

const myMap = map[int]string{
    1: "one",
    2: "two",
    3: "three",
}

This is the error

map[int]string{…} (value of type map[int]string) is not constant
Hymns For Disco
  • 7,530
  • 2
  • 17
  • 33
samol
  • 18,950
  • 32
  • 88
  • 127
  • 3
    golang doesn't allow const maps. here is a good explanation why https://www.reddit.com/r/golang/comments/993ujz/why_is_not_possible_to_create_a_constant_map/ – Rahim Oct 10 '20 at 20:29

6 Answers6

252

In Go, a map unfortunately cannot be const. You can declare it as a regular variable like this with the var keyword:

var myMap = map[int]string{
    1: "one",
    2: "two",
    3: "three",
}

Inside a function, you may declare it with the short assignment syntax:

func main() {
    myMap := map[int]string{
        1: "one",
        2: "two",
        3: "three",
    }
}

Try it out on the Go playground.

Hymns For Disco
  • 7,530
  • 2
  • 17
  • 33
squiguy
  • 32,370
  • 6
  • 56
  • 63
36

You can create constants in many different ways:

const myString = "hello"
const pi = 3.14 // untyped constant
const life int = 42 // typed constant (can use only with ints)

You can also create a enum constant:

const ( 
   First = 1
   Second = 2
   Third = 4
)

You can not create constants of maps, arrays and it is written in effective go:

Constants in Go are just that—constant. They are created at compile time, even when defined as locals in functions, and can only be numbers, characters (runes), strings or booleans. Because of the compile-time restriction, the expressions that define them must be constant expressions, evaluatable by the compiler. For instance, 1<<3 is a constant expression, while math.Sin(math.Pi/4) is not because the function call to math.Sin needs to happen at run time.

Salvador Dali
  • 214,103
  • 147
  • 703
  • 753
  • 2
    so it's more like a C++11 constexpr... why math.Sin is not a constexpr function, then! – Francesco Dondi Jun 21 '16 at 23:40
  • Your statements are correct, but the question was about creating a map that is constant. – jzer7 Nov 13 '16 at 17:36
  • 10
    @jzer7 can you explain me why my answer is irrelevant? He asked how to create something, I told him that this is not possible. Explained what is possible and gave a citation from the docs why exactly is it not possible to do what he wants. – Salvador Dali Nov 13 '16 at 22:16
18

You may emulate a map with a closure:

package main

import (
    "fmt"
)

// http://stackoverflow.com/a/27457144/10278

func romanNumeralDict() func(int) string {
    // innerMap is captured in the closure returned below
    innerMap := map[int]string{
        1000: "M",
        900:  "CM",
        500:  "D",
        400:  "CD",
        100:  "C",
        90:   "XC",
        50:   "L",
        40:   "XL",
        10:   "X",
        9:    "IX",
        5:    "V",
        4:    "IV",
        1:    "I",
    }

    return func(key int) string {
        return innerMap[key]
    }
}

func main() {
    fmt.Println(romanNumeralDict()(10))
    fmt.Println(romanNumeralDict()(100))

    dict := romanNumeralDict()
    fmt.Println(dict(400))
}

Try it on the Go playground

pestophagous
  • 4,069
  • 3
  • 33
  • 42
oleber
  • 1,089
  • 4
  • 12
  • 25
  • 4
    (TestMostSoldRecommender?) – twotwotwo Dec 13 '14 at 09:00
  • 2
    It is in fact a possible solution. However, since the author did not explain anything (and put everything inside a weirdly-named test case), the answer looks incorrect. The logic is: (1) Create an anonymous function (2) The anonymous function encapsulates the `map` (3) The anonymous function returns "a function that accepts an int and returns a string" (4) The returned function does the int -> string mapping by using the `map` (5) Execute the anonymous function immediately and assign the returned function to a variable. This variable could be used like a function, and the effect is like a map. – Siu Ching Pong -Asuka Kenji- Dec 09 '15 at 16:03
  • this is not ideal, we still missing important map capabilities, such as `len` , key list etc – kao peter Jun 12 '22 at 02:13
4

And as suggested above by Siu Ching Pong -Asuka Kenji with the function which in my opinion makes more sense and leaves you with the convenience of the map type without the function wrapper around:

   // romanNumeralDict returns map[int]string dictionary, since the return
       // value is always the same it gives the pseudo-constant output, which
       // can be referred to in the same map-alike fashion.
       var romanNumeralDict = func() map[int]string { return map[int]string {
            1000: "M",
            900:  "CM",
            500:  "D",
            400:  "CD",
            100:  "C",
            90:   "XC",
            50:   "L",
            40:   "XL",
            10:   "X",
            9:    "IX",
            5:    "V",
            4:    "IV",
            1:    "I",
          }
        }

        func printRoman(key int) {
          fmt.Println(romanNumeralDict()[key])
        }

        func printKeyN(key, n int) {
          fmt.Println(strings.Repeat(romanNumeralDict()[key], n))
        }

        func main() {
          printRoman(1000)
          printRoman(50)
          printKeyN(10, 3)
        }

Try this at play.golang.org.

Denis Volin
  • 206
  • 1
  • 8
0

I know this is not the nicest looking code, but you can achieve somewhat of a constant map-like thing using anonymous structs and closures:

Voila!

type MapGet[T any, V any] func(T) (V, bool)

var (
    myMap = struct {
        Get MapGet[int, string]
    }{
        Get: func(mp map[int]string) MapGet[int, string] {
            return func(key int) (string, bool) {
                v, found := mp[key]
                return v, found
            }
        }(map[int]string{
            1: "one",
            2: "two",
            3: "three",
        }),
    }
)

func main() {
    v, _ := myMap.Get(1)
    fmt.Println(v)
}

https://goplay.tools/snippet/SrAMRsBOPn9

smac89
  • 39,374
  • 15
  • 132
  • 179
-5

As stated above to define a map as constant is not possible. But you can declare a global variable which is a struct that contains a map.

The Initialization would look like this:

var romanNumeralDict = struct {
    m map[int]string
}{m: map[int]string {
    1000: "M",
    900: "CM",
    //YOUR VALUES HERE
}}

func main() {
    d := 1000
    fmt.Printf("Value of Key (%d): %s", d, romanNumeralDict.m[1000])
}
inde
  • 7
  • 1