Realizing that Go constants are always materialized into values
I recently read Global Constant Maps and Slices in Go
(via),
which starts by noting that Go doesn't let you create const
maps
or slices and then works around that by having an access function
that returns a constant slice (or map):
const rateLimit = 10func getSupportedNetworks() []string { return []string{"facebook", "twitter", "instagram"} }
When I read the article, my instinctive reaction was that that's
not actually a constant because the caller can always change it
(although if you call getSupportedNetworks()
again you get a new
clean original slice). Then I thought more about real Go constants
like rateLimit
and realized that they have this behavior too,
because any time you use a Go constant, it's materialized into a
mutable value.
Obviously if you assign the rateLimit
constant to a variable, you
can then change the variable later; the same is true of assigning
it to a struct field. If you call a function and pass rateLimit
as one argument, the function receives it as an argument value and
can change it. If a function returns rateLimit
, the caller gets
back a value and can again change it. This is no different with the
slice that getSupportedNetworks()
returns.
The difference between using rateLimit
and using the return value
from getSupportedNetworks
is that the latter can be mutated through
a second reference without the explicit use of Go pointers:
func main() { a := rateLimit b := &a *b += 10 c := getSupportedNetworks() d := c d[1] = "mastodon" fmt.Println(a, c) }
But this is not a difference between true Go constants and our emulated constant slice, it's a difference in the handling of the types involved. Maps and slices are special this way, but other Go values are not.
(Slices are also mutable at a distance in other ways.)
PS: Go constants can't have their address taken with '&
', but
they aren't the only sorts of unaddressable values in Go. In theory we could make getSupportedNetworks()
return an unaddressable value by making its return value be
'[3]string
', as we've seen before; in
practice you almost certainly don't want to do that for various
reasons.
(This seems like an obvious observation now that I've thought about it, but I hadn't really thought about it before reading the article and having my reflexive first reaction.)
|
|