Learn computer science by building real projects with modern technologies

How to Create Constant Maps, Slices, & Arrays in Golang

By Lane Wagner on Oct 21, 2019

For the most part, I’ve found that Go developers are pretty good at using global constants for configuration rather than global variables.

However, a problem arises when we want a constant version of some of the more complex types. The Go compiler does not allow us to create array, map, or slice constants. After realizing this, many developers decide to use a dangerous global variable.

In this article, we will explore some alternative options to effectively make constant maps, slices, and arrays, albeit with some trade-offs.

Spoiler - TL;DR

If you’re looking for a quick answer: Go doesn’t support const arrays.

It also doesn’t support constant maps, slices, or other complex types. From the official specification:

There are boolean constants, rune constants, integer constants, floating-point constants, complex constants, and string constants. Rune, integer, floating-point, and complex constants are collectively called numeric constants.

The solution, which I explain in more detail later, is to use initialization functions. While of course the variables once created are still changeable, at least you can always get a new copy with the guarantee that it has the correct values.

Example of const array in Go

func getArray() [5]int {
    return [5]int{10, 20, 30, 40, 50} 
}

Example of const slice in Go

func getSlice() []string {
    return []string{"hello", "world"}
}

Example of const map in Go

func getMap() map[string]int {
    return map[string]int{
        "truck": 5,
        "car": 7,
    }
}

With the quick answer out of the way, let’s explore why this is a good solution in many cases.

A Brief Refresher on Globals and Constants

package foo

// this is a global constant
const safeRateLimit = 10

// this is a global variable
var dangerousRateLimit = 10

When setting configuration globals, which should be read-only, there’s no good reason to use a global variable. By using a variable instead of a constant you:

Most people already know this about global variables thankfully, and switching global variables to global constants is a fairly straightforward task.

Learn Go by writing Go code

I'm a senior engineer learning Go, and the pace of Boot.dev's Go courses have been perfect for me. The diverse community in Discord is a blast, and other members are quick to help out with detailed answers and explanations.

- Daniel Gerep from Cassia, Brasil

What If I Want A Global Array, Map, or Slice?

global slice

Let’s assume the following situation:

We have a program that needs two sets of configurations. The configurations are:

Now that we know a bit about the configurations we make the following decisions:

We then write the following code:

package main

const rateLimit = 10

const supportedNetworks = []string{"facebook", "twitter", "instagram"}

Much to our surprise, when we try to compile this code we get the following error:

const initializer []string literal is not a constant

Unlike constants in JavaScript, Go doesn’t allow complex types like slices, maps, or arrays to be constant! Our first instinct may be to lazily switch it to a variable, and add a comment:

package main

const rateLimit = 10

// this is meant to be constant! Please don't mutate it!
var supportedNetworks = []string{"facebook", "twitter", "instagram"}

Whenever we find ourselves leaving comments like this, we should be aware we are doing something wrong.

The Better Solution for Constants in Go

It’s much better to use an initializer function (not to be confused with Go’s conventional init() function). An initializer function is a function that simply declares something and returns it. Like I explained above in the tl;dr a good solution to our problem would be as follows:

package main

const rateLimit = 10

func getSupportedNetworks() []string {
	return []string{"facebook", "twitter", "instagram"}
}

Now, anywhere in the program, we can use the result of getSupportedNetworks() and we know that there is no way we can get a mutated value.

Obviously one of the biggest downsides to this approach is that to get a new copy of the configuration you’re literally creating a new copy and making a function call. In the vast majority of cases this should be fine - if it’s truly just a configuration you probably won’t need to be accessing it all the time. That said, if you’re rapidly making new copies it could become a performance issue.

Good Practices

Being able to keep access to maps and slices that are effectively constant can make your code easier to read, and more importantly, much less error-prone. One of the most sought-after traits of a computer scientist for high-end coding jobs is the ability to read, write, and refactor code so that it’s more maintainable and easier to understand.

Learn to code by building real projects

Related Reading