Constants | |

25 Aug 2014 | |

Tags: constants | |

Rob Pike | |

* Introduction | |

Go is a statically typed language that does not permit operations that mix numeric types. | |

You can't add a `float64` to an `int`, or even an `int32` to an `int`. | |

Yet it is legal to write `1e6*time.Second` or `math.Exp(1)` or even `1<<('\t'+2.0)`. | |

In Go, constants, unlike variables, behave pretty much like regular numbers. | |

This post explains why that is and what it means. | |

* Background: C | |

In the early days of thinking about Go, we talked about a number of problems caused by the way C and its descendants let you mix and match numeric types. | |

Many mysterious bugs, crashes, and portability problems are caused by expressions that combine integers of different sizes and "signedness". | |

Although to a seasoned C programmer the result of a calculation like | |

unsigned int u = 1e9; | |

long signed int i = -1; | |

... i + u ... | |

may be familiar, it isn't _a_priori_ obvious. | |

How big is the result? | |

What is its value? | |

Is it signed or unsigned? | |

Nasty bugs lurk here. | |

C has a set of rules called "the usual arithmetic conversions" and it is an indicator of their subtlety that they have changed over the years (introducing yet more bugs, retroactively). | |

When designing Go, we decided to avoid this minefield by mandating that there is _no_ mixing of numeric types. | |

If you want to add `i` and `u`, you must be explicit about what you want the result to be. | |

Given | |

var u uint | |

var i int | |

you can write either `uint(i)+u` or `i+int(u)`, with both the meaning and type of the addition clearly expressed, but unlike in C you cannot write `i+u`. | |

You can't even mix `int` and `int32`, even when `int` is a 32-bit type. | |

This strictness eliminates a common cause of bugs and other failures. | |

It is a vital property of Go. | |

But it has a cost: it sometimes requires programmers to decorate their code with clumsy numeric conversions to express their meaning clearly. | |

And what about constants? | |

Given the declarations above, what would make it legal to write `i` `=` `0` or `u` `=` `0`? | |

What is the _type_ of `0`? | |

It would be unreasonable to require constants to have type conversions in simple contexts such as `i` `=` `int(0)`. | |

We soon realized the answer lay in making numeric constants work differently from how they behave in other C-like languages. | |

After much thinking and experimentation, we came up with a design that we believe feels right almost always, freeing the programmer from converting constants all the time yet being able to write things like `math.Sqrt(2)` without being chided by the compiler. | |

In short, constants in Go just work, most of the time anyway. | |

Let's see how that happens. | |

* Terminology | |

First, a quick definition. | |

In Go, `const` is a keyword introducing a name for a scalar value such as `2` or `3.14159` or `"scrumptious"`. | |

Such values, named or otherwise, are called _constants_ in Go. | |

Constants can also be created by expressions built from constants, such as `2+3` or `2+3i` or `math.Pi/2` or `("go"+"pher")`. | |

Some languages don't have constants, and others have a more general definition of constant or application of the word `const`. | |

In C and C++, for instance, `const` is a type qualifier that can codify more intricate properties of more intricate values. | |

But in Go, a constant is just a simple, unchanging value, and from here on we're talking only about Go. | |

* String constants | |

There are many kinds of numeric constants—integers, floats, runes, signed, unsigned, imaginary, complex—so let's start with a simpler form of constant: strings. | |

String constants are easy to understand and provide a smaller space in which to explore the type issues of constants in Go. | |

A string constant encloses some text between double quotes. | |

(Go also has raw string literals, enclosed by backquotes ``````, but for the purpose of this discussion they have all the same properties.) | |

Here is a string constant: | |

"Hello, 世界" | |

(For much more detail about the representation and interpretation of strings, see [[https://blog.golang.org/strings][this blog post]].) | |

What type does this string constant have? | |

The obvious answer is `string`, but that is _wrong_. | |

This is an _untyped_string_constant_, which is to say it is a constant textual value that does not yet have a fixed type. | |

Yes, it's a string, but it's not a Go value of type `string`. | |

It remains an untyped string constant even when given a name: | |

const hello = "Hello, 世界" | |

After this declaration, `hello` is also an untyped string constant. | |

An untyped constant is just a value, one not yet given a defined type that would force it to obey the strict rules that prevent combining differently typed values. | |

It is this notion of an _untyped_ constant that makes it possible for us to use constants in Go with great freedom. | |

So what, then, is a _typed_ string constant? | |

It's one that's been given a type, like this: | |

const typedHello string = "Hello, 世界" | |

Notice that the declaration of `typedHello` has an explicit `string` type before the equals sign. | |

This means that `typedHello` has Go type `string`, and cannot be assigned to a Go variable of a different type. | |

That is to say, this code works: | |

.play -edit constants/string1.go /START/,/STOP/ | |

but this does not: | |

.play -edit constants/string2.go /START/,/STOP/ | |

The variable `m` has type `MyString` and cannot be assigned a value of a different type. | |

It can only be assigned values of type `MyString`, like this: | |

.play -edit constants/string3.go /START/,/STOP/ | |

or by forcing the issue with a conversion, like this: | |

.play -edit constants/string4.go /START/,/STOP/ | |

Returning to our _untyped_ string constant, it has the helpful property that, since it has no type, assigning it to a typed variable does not cause a type error. | |

That is, we can write | |

m = "Hello, 世界" | |

or | |

m = hello | |

because, unlike the typed constants `typedHello` and `myStringHello`, the untyped constants `"Hello,`世界"` and `hello` _have_no_type_. | |

Assigning them to a variable of any type compatible with strings works without error. | |

These untyped string constants are strings, of course, so they can only be used where a string is allowed, but they do not have _type_ `string`. | |

* Default type | |

As a Go programmer, you have certainly seen many declarations like | |

str := "Hello, 世界" | |

and by now you might be asking, "if the constant is untyped, how does `str` get a type in this variable declaration?" | |

The answer is that an untyped constant has a default type, an implicit type that it transfers to a value if a type is needed where none is provided. | |

For untyped string constants, that default type is obviously `string`, so | |

str := "Hello, 世界" | |

or | |

var str = "Hello, 世界" | |

means exactly the same as | |

var str string = "Hello, 世界" | |

One way to think about untyped constants is that they live in a kind of ideal space of values, a space less restrictive than Go's full type system. | |

But to do anything with them, we need to assign them to variables, and when that happens the _variable_ (not the constant itself) needs a type, and the constant can tell the variable what type it should have. | |

In this example, `str` becomes a value of type `string` because the untyped string constant gives the declaration its default type, `string`. | |

In such a declaration, a variable is declared with a type and initial value. | |

Sometimes when we use a constant, however, the destination of the value is not so clear. | |

For instance consider this statement: | |

.play -edit constants/default1.go /START/,/STOP/ | |

The signature of `fmt.Printf` is | |

func Printf(format string, a ...interface{}) (n int, err error) | |

which is to say its arguments (after the format string) are interface values. | |

What happens when `fmt.Printf` is called with an untyped constant is that an interface value is created | |

to pass as an argument, and the concrete type stored for that argument is the default type of the constant. | |

This process is analogous to what we saw earlier when declaring an initialized value using an untyped string constant. | |

You can see the result in this example, which uses the format `%v` to print the value and `%T` to print the type of the value being passed to `fmt.Printf`: | |

.play -edit constants/default2.go /START/,/STOP/ | |

If the constant has a type, that goes into the interface, as this example shows: | |

.play -edit constants/default3.go /START/,/STOP/ | |

(For more information about how interface values work, see the first sections of [[https://blog.golang.org/laws-of-reflection][this blog post]].) | |

In summary, a typed constant obeys all the rules of typed values in Go. | |

On the other hand, an untyped constant does not carry a Go type in the same way and can be mixed and matched more freely. | |

It does, however, have a default type that is exposed when, and only when, no other type information is available. | |

* Default type determined by syntax | |

The default type of an untyped constant is determined by its syntax. | |

For string constants, the only possible implicit type is `string`. | |

For [[https://golang.org/ref/spec#Numeric_types][numeric constants]], the implicit type has more variety. | |

Integer constants default to `int`, floating-point constants `float64`, rune constants to `rune` (an alias for `int32`), and imaginary constants to `complex128`. | |

Here's our canonical print statement used repeatedly to show the default types in action: | |

.play -edit constants/syntax.go /START/,/STOP/ | |

(Exercise: Explain the result for `'x'`.) | |

* Booleans | |

Everything we said about untyped string constants can be said for untyped boolean constants. | |

The values `true` and `false` are untyped boolean constants that can be assigned to any boolean variable, | |

but once given a type, boolean variables cannot be mixed: | |

.play -edit constants/bool.go /START/,/STOP/ | |

Run the example and see what happens, then comment out the "Bad" line and run it again. | |

The pattern here follows exactly that of string constants. | |

* Floats | |

Floating-point constants are just like boolean constants in most respects. | |

Our standard example works as expected in translation: | |

.play -edit constants/float1.go /START/,/STOP/ | |

One wrinkle is that there are _two_ floating-point types in Go: `float32` and `float64`. | |

The default type for a floating-point constant is `float64`, although an untyped floating-point | |

constant can be assigned to a `float32` value just fine: | |

.play -edit constants/float2.go /START/,/STOP/ | |

Floating-point values are a good place to introduce the concept of overflow, or the range of values. | |

Numeric constants live in an arbitrary-precision numeric space; they are just regular numbers. | |

But when they are assigned to a variable the value must be able to fit in the destination. | |

We can declare a constant with a very large value: | |

.code constants/float3.go /Huge/ | |

—that's just a number, after all—but we can't assign it or even print it. This statement won't even compile: | |

.play -edit constants/float3.go /Println/ | |

The error is, "constant 1.00000e+1000 overflows float64", which is true. | |

But `Huge` might be useful: we can use it in expressions with other constants and use the value of those expressions if the result | |

can be represented in the range of a `float64`. | |

The statement, | |

.play -edit constants/float4.go /Println/ | |

prints `10`, as one would expect. | |

In a related way, floating-point constants may have very high precision, so that arithmetic involving them is more accurate. | |

The constants defined in the [[https://golang.org/pkg/math][math]] package are given with many more digits than are | |

available in a `float64`. Here is the definition of `math.Pi`: | |

Pi = 3.14159265358979323846264338327950288419716939937510582097494459 | |

When that value is assigned to a variable, some of the precision will be lost; the assignment will create the `float64` (or `float32`) | |

value closest to the high-precision value. This snippet | |

.play -edit constants/float5.go /START/,/STOP/ | |

prints `3.141592653589793`. | |

Having so many digits available means that calculations like `Pi/2` or other more intricate evaluations can carry more precision | |

until the result is assigned, making calculations involving constants easier to write without losing precision. | |

It also means that there is no occasion in which the floating-point corner cases like infinities, | |

soft underflows, and `NaNs` arise in constant expressions. | |

(Division by a constant zero is a compile-time error, and when everything is a number there's no such thing as "not a number".) | |

* Complex numbers | |

Complex constants behave a lot like floating-point constants. | |

Here's a version of our now-familiar litany translated into complex numbers: | |

.play -edit constants/complex1.go /START/,/STOP/ | |

The default type of a complex number is `complex128`, the larger-precision version composed of two `float64` values. | |

For clarity in our example, we wrote out the full expression `(0.0+1.0i)`, but this value can be shortened to `0.0+1.0i`, | |

`1.0i` or even `1i`. | |

Let's play a trick. | |

We know that in Go, a numeric constant is just a number. | |

What if that number is a complex number with no imaginary part, that is, a real? | |

Here's one: | |

.code constants/complex2.go /const Two/ | |

That's an untyped complex constant. | |

Even though it has no imaginary part, the _syntax_ of the expression defines it to have default type `complex128`. | |

Therefore, if we use it to declare a variable, the default type will be `complex128`. The snippet | |

.play -edit constants/complex2.go /START/,/STOP/ | |

prints `complex128:` `(2+0i)`. | |

But numerically, `Two` can be stored in a scalar floating-point number, a `float64` or `float32`, with no loss of information. | |

Thus we can assign `Two` to a `float64`, either in an initialization or an assignment, without problems: | |

.play -edit constants/complex3.go /START/,/STOP/ | |

The output is `2` `and` `2`. | |

Even though `Two` is a complex constant, it can be assigned to scalar floating-point variables. | |

This ability for a constant to "cross" types like this will prove useful. | |

* Integers | |

At last we come to integers. | |

They have more moving parts—[[https://golang.org/ref/spec#Numeric_types][many sizes, signed or unsigned, and more]]—but they play by the same rules. | |

For the last time, here is our familiar example, using just `int` this time: | |

.play -edit constants/int1.go /START/,/STOP/ | |

The same example could be built for any of the integer types, which are: | |

int int8 int16 int32 int64 | |

uint uint8 uint16 uint32 uint64 | |

uintptr | |

(plus the aliases `byte` for `uint8` and `rune` for `int32`). | |

That's a lot, but the pattern in the way constants work should be familiar enough by now that you can see how things will play out. | |

As mentioned above, integers come in a couple of forms and each form has its own default type: `int` for simple constants like `123` or `0xFF` or `-14` | |

and `rune` for quoted characters like 'a', '世' or '\r'. | |

No constant form has as its default type an unsigned integer type. | |

However, the flexibility of untyped constants means we can initialize unsigned integer variables using simple constants as long as we are clear about the type. | |

It's analogous to how we can initialize a `float64` using a complex number with zero imaginary part. | |

Here are several different ways to initialize a `uint`; all are equivalent, but all must mention the type explicitly for the result to be unsigned. | |

var u uint = 17 | |

var u = uint(17) | |

u := uint(17) | |

Similarly to the range issue mentioned in the section on floating-point values, not all integer values can fit in all integer types. | |

There are two problems that might arise: the value might be too large, or it might be a negative value being assigned to an unsigned integer type. | |

For instance, `int8` has range -128 through 127, so constants outside of that range can never be assigned to a variable of type `int8`: | |

.play -edit constants/int2.go /var/ | |

Similarly, `uint8`, also known as `byte`, has range 0 through 255, so a large or negative constant cannot be assigned to a `uint8`: | |

.play -edit constants/int3.go /var/ | |

This type-checking can catch mistakes like this one: | |

.play -edit constants/int4.go /START/,/STOP/ | |

If the compiler complains about your use of a constant, it's likely a real bug like this. | |

* An exercise: The largest unsigned int | |

Here is an informative little exercise. | |

How do we express a constant representing the largest value that fits in a `uint`? | |

If we were talking about `uint32` rather than `uint`, we could write | |

const MaxUint32 = 1<<32 - 1 | |

but we want `uint`, not `uint32`. | |

The `int` and `uint` types have equal unspecified numbers of bits, either 32 or 64. | |

Since the number of bits available depends on the architecture, we can't just write down a single value. | |

Fans of [[http://en.wikipedia.org/wiki/Two's_complement][two's-complement arithmetic]], | |

which Go's integers are defined to use, know that the representation of `-1` has all its bits set to 1, | |

so the bit pattern of `-1` is internally the same as that of the | |

largest unsigned integer. | |

We therefore might think we could write | |

.play -edit constants/exercise1.go /const/ | |

but that is illegal because -1 cannot be represented by an unsigned variable; `-1` is not in the range of unsigned values. | |

A conversion won't help either, for the same reason: | |

.play -edit constants/exercise2.go /const/ | |

Even though at run-time a value of -1 can be converted to an unsigned integer, the rules | |

for constant [[https://golang.org/ref/spec#Conversions][conversions]] forbid this kind of coercion at compile time. | |

That is to say, this works: | |

.play -edit constants/exercise3.go /START/,/STOP/ | |

but only because `v` is a variable; if we made `v` a constant, even an untyped constant, we'd be back in forbidden territory: | |

.play -edit constants/exercise4.go /START/,/STOP/ | |

We return to our previous approach, but instead of `-1` we try `^0`, the bitwise negation of an arbitrary number of zero bits. | |

But that fails too, for a similar reason: | |

In the space of numeric values, | |

`^0` represents an infinite number of ones, so we lose information if we assign that to any fixed-size integer: | |

.play -edit constants/exercise5.go /const/ | |

How then do we represent the largest unsigned integer as a constant? | |

The key is to constrain the operation to the number of bits in a `uint` and avoiding | |

values, such as negative numbers, that are not representable in a `uint`. | |

The simplest `uint` value is the typed constant `uint(0)`. | |

If `uints` have 32 or 64 bits, `uint(0)` has 32 or 64 zero bits accordingly. | |

If we invert each of those bits, we'll get the correct number of one bits, which is the largest `uint` value. | |

Therefore we don't flip the bits of the untyped constant `0`, we flip the bits of the typed constant `uint(0)`. | |

Here, then, is our constant: | |

.play -edit constants/exercise6.go /START/,/STOP/ | |

Whatever the number of bits it takes to represent a `uint` in the current execution environment | |

(on the [[https://blog.golang.org/playground][playground]], it's 32), | |

this constant correctly represents the largest value a variable of type `uint` can hold. | |

If you understand the analysis that got us to this result, you understand all the important points about constants in Go. | |

* Numbers | |

The concept of untyped constants in Go means that all the numeric constants, whether integer, floating-point, complex, or even character values, | |

live in a kind of unified space. | |

It's when we bring them to the computational world of variables, assignments, and operations that the actual types matter. | |

But as long as we stay in the world of numeric constants, we can mix and match values as we like. | |

All these constants have numeric value 1: | |

1 | |

1.000 | |

1e3-99.0*10-9 | |

'\x01' | |

'\u0001' | |

'b' - 'a' | |

1.0+3i-3.0i | |

Therefore, although they have different implicit default types, written as untyped constants they can be assigned to a variable of any integer type: | |

.play -edit constants/numbers1.go /START/,/STOP/ | |

The output from this snippet is: `1`1`1`1`1`(1+0i)`1`. | |

You can even do nutty stuff like | |

.play -edit constants/numbers2.go /START/,/STOP/ | |

which yields 145.5, which is pointless except to prove a point. | |

But the real point of these rules is flexibility. | |

That flexibility means that, despite the fact that in Go it is illegal in the same expression to mix floating-point and integer variables, | |

or even `int` and `int32` variables, it is fine to write | |

sqrt2 := math.Sqrt(2) | |

or | |

const millisecond = time.Second/1e3 | |

or | |

bigBufferWithHeader := make([]byte, 512+1e6) | |

and have the results mean what you expect. | |

Because in Go, numeric constants work as you expect: like numbers. | |