For the most part, Go developers are pretty good about using constants for global configuration, rather than global variables. A problem arises however when we want a global constant slice or map. The go compiler doesn't allow these more complex types to be set as constant. Many developers, upon making this realization, decide to then use a dangerous global variable. In this article we will explore a better option.
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 is no 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.
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 configs 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
Go doesn't allow complex types like slices and maps 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.
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. 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.
Thanks for reading!
Previously published at https://qvault.io/2019/10/21/how-to-global-constant-maps-and-slices-in-go/