Question
I am trying to declare a constant in Go, but the compiler reports an error.
This is my code:
const myMap = map[int]string{
1: "one",
2: "two",
3: "three",
}
The error message is:
map[int]string{…} (value of type map[int]string) is not constant
Why does this happen in Go, and what is the correct way to define a map that should not be changed?
Short Answer
By the end of this page, you will understand why map values cannot be declared as const in Go, what kinds of values can be constants, and the usual alternatives developers use when they want read-only map-like data.
Concept
In Go, const is only for values that the compiler can fully determine at compile time. These are simple, immutable values such as:
- numbers
- booleans
- strings
- certain constant expressions built from them
A map is different. A map is a runtime data structure with internal state. It is a reference type, which means it points to underlying data managed by the Go runtime. Because of that, a map is not a constant value and cannot be declared with const.
Your code fails because this:
map[int]string{
1: "one",
2: "two",
3: "three",
}
creates a map value at runtime, not a compile-time constant.
In real Go programs, when you want fixed lookup data, you usually do one of these:
- declare a package-level
var - avoid exporting the map directly
- expose helper functions for safe access
- use a
switchif the mapping is small and truly fixed
This matters because Go separates constant values from mutable runtime data structures. Understanding that difference helps you choose the correct tool and avoid compiler errors.
Mental Model
Think of a Go constant like a number printed in a textbook: it never changes, and the compiler knows it in advance.
A map is more like a whiteboard dictionary in an office:
- it exists at runtime
- it can be updated
- it has internal structure
- people can write and erase entries
Even if you intend not to modify the whiteboard, it is still a whiteboard, not printed text. That is why Go does not let you call it a constant.
Syntax and Examples
In Go, constants and maps use different declarations.
Valid constants
const pi = 3.14
const appName = "My App"
const maxRetries = 3
These work because they are compile-time constant values.
Invalid constant map
const myMap = map[int]string{
1: "one",
2: "two",
}
This does not compile because a map is not a constant.
Correct way: use var
var myMap = map[int]string{
1: "one",
2: "two",
3: "three",
}
This creates a normal map variable.
Safer pattern: keep the map unexported and provide a function
Step by Step Execution
Consider this example:
package main
import "fmt"
var numberWords = map[int]string{
1: "one",
2: "two",
3: "three",
}
func main() {
value, ok := numberWords[2]
fmt.Println(value, ok)
}
Here is what happens step by step:
- The program starts.
- The package-level variable
numberWordsis created. - Go allocates and initializes the map with three key-value pairs.
- In
main, the expressionnumberWords[2]looks up key2. - Because key
2exists, Go returns:"two"as the valuetrueforok
fmt.Println(value, ok)prints:
Real World Use Cases
Developers often need lookup data that should stay stable during program execution.
Common examples
- HTTP status code descriptions
- country code to country name mappings
- file extension to MIME type lookups
- internal configuration labels
- enum-like display names
Example: status text lookup
var statusText = map[int]string{
200: "OK",
404: "Not Found",
500: "Internal Server Error",
}
Example: role labels
var roleLabels = map[string]string{
"admin": "Administrator",
"editor": "Editor",
"user": "User",
}
In these cases, the map is usually declared once and treated as read-only by convention, even though it is technically mutable.
Real Codebase Usage
In real Go codebases, developers rarely need a true constant map. Instead, they use patterns that make mutation unlikely or controlled.
Common patterns
1. Package-level unexported variable
var statusNames = map[int]string{
1: "pending",
2: "approved",
3: "rejected",
}
Because the variable starts with a lowercase letter, it is not exported from the package.
2. Accessor function
func StatusName(code int) (string, bool) {
s, ok := statusNames[code]
return s, ok
}
This gives controlled read access.
3. Defensive copy when returning maps
If you must return a map, return a copy so callers cannot change the original.
func StatusNames() map[int]string {
copyMap := make([], (statusNames))
k, v := statusNames {
copyMap[k] = v
}
copyMap
}
Common Mistakes
Mistake 1: Trying to use const with a map
Broken code:
const myMap = map[int]string{
1: "one",
}
Why it fails:
- maps are runtime values
constonly supports compile-time constants
Fix:
var myMap = map[int]string{
1: "one",
}
Mistake 2: Assuming var means the data must change
Some beginners think var means “this should be modified.” That is not true.
var config = map[string]string{
"env": "prod",
}
This is still fine even if you never change it.
Mistake 3: Exposing the map directly
Comparisons
Constants vs variables in Go
| Feature | const | var |
|---|---|---|
| Known at compile time | Yes | Not necessarily |
| Can hold numbers, strings, booleans | Yes | Yes |
| Can hold maps | No | Yes |
| Can be changed later | No | Yes |
| Requires runtime allocation | No | Sometimes |
Map vs switch for fixed lookups
| Option | Best for | Mutable |
|---|
Cheat Sheet
// Valid constants
const name = "Go"
const max = 10
const enabled = true
// Invalid: maps cannot be constants
// const labels = map[int]string{1: "one"}
// Use var instead
var labels = map[int]string{
1: "one",
2: "two",
}
Key rules
constis only for compile-time constant values.- Maps are runtime reference types.
- A map cannot be declared with
const. - Use
varfor maps. - If you want read-only behavior, hide the map and expose functions.
Safe access pattern
value, ok := labels[1]
if ok {
fmt.Println(value)
}
Read-only style pattern
var labels = map[int]string{: }
(, ) {
v, ok := labels[n]
v, ok
}
FAQ
Why can't a map be a constant in Go?
Because a map is a runtime data structure, not a compile-time constant value.
What types can be const in Go?
Basic constant-compatible values such as numbers, strings, booleans, and constant expressions based on them.
How do I make a read-only map in Go?
Go has no built-in read-only map. The usual approach is to keep the map private and provide functions to read from it.
Should I use var even if the map never changes?
Yes. That is normal in Go. A map that you intend not to modify is still declared with var.
Is switch better than a map for fixed values?
Sometimes. If the mapping is very small and truly fixed, switch can be simpler and safer.
Can other packages modify my map?
Yes, if you export the map directly. To prevent that, keep it unexported and expose accessor functions instead.
Does returning a map from a function make it safe?
Not if you return the original map. Callers can still modify it. Return a copy if you need isolation.
Mini Project
Description
Build a small number-to-word lookup utility in Go. This project demonstrates the correct way to store fixed lookup data using a package-level map and a helper function instead of trying to use const with a map.
Goal
Create a Go program that looks up number names safely and reports whether a number exists in the mapping.
Requirements
- Declare a map using
var, notconst - Store at least five number-to-word pairs
- Write a function that returns both the word and a boolean indicating whether the key exists
- In
main, test the function with one existing key and one missing key
Keep learning
Related questions
Automatic Build Versioning in Go: Embed Incrementing Build Numbers
Learn how to add automatic build versioning in Go using linker flags, build metadata, CI counters, and Git-based version values.
Blank Identifier Imports in Go: What `_` Means in an Import Statement
Learn what `_` means in a Go import, why blank identifier imports run package init code, and when to use them safely.
Calling Functions Across Files in the Same Go Package
Learn how Go uses packages across multiple files, why functions may appear undefined, and how to organize code correctly.