223

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
1

7 Answers 7

272

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.

1
  • 2
    I think the author means how to protect the map from unwanted changes
    – S2201
    Commented Feb 23, 2020 at 16:35
38

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.

3
  • 3
    so it's more like a C++11 constexpr... why math.Sin is not a constexpr function, then! Commented Jun 21, 2016 at 23:40
  • Your statements are correct, but the question was about creating a map that is constant.
    – jzer7
    Commented Nov 13, 2016 at 17:36
  • 12
    @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. Commented Nov 13, 2016 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

3
  • 4
    (TestMostSoldRecommender?)
    – twotwotwo
    Commented Dec 13, 2014 at 9: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. Commented Dec 9, 2015 at 16:03
  • 1
    this is not ideal, we still missing important map capabilities, such as len , key list etc
    – kao peter
    Commented Jun 12, 2022 at 2:13
5

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.

1

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

0

I used to do it in this way

package mypackage

var _myMap = map[string]int{
    "apple":  1,
    "banana": 2,
    "cherry": 3,
}

// MyMap returns an immutable copy of the map
func MyMap() map[string]int {
    return _myMap
}
-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])
}
2
  • 4
    Why not just the make the map the global variable? Why wrap it in the struct? Commented Feb 3, 2017 at 14:12
  • 3
    This doesn't make the map constant, you can still do romanNumeralDict.m[1000] = "New value"
    – brando
    Commented Jan 17, 2018 at 21:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.