Constants

Constants

Constants are not variables

One of the really interesting things about constants, for me is that they only exist at compile time. Constants are something that really have a much different feel and flavor to than your common variables.

Most of the time we think about constants as being read-only variables, and it’s absolutely not the case in Go.

Two types of constants

There’s two types of constants, constants of a kind and constants of a type.

// Untyped Constants.
const ui = 12345    // kind: integer
const uf = 3.141592 // kind: floating-point

// Typed Constants.
const ti int = 12345        // type: int
const tf float64 = 3.141592 // type: float64

The difference between constants of a kind and of a type, are that constants of a kind can be implicitly converted by the compiler.

Explicit and implicit conversions

Kind Promotion will tell you how things promote so floats promote over ints and types always promote over kind.

// Variable answer will of type float64.
var answer = 3 * 0.333 // KindFloat(3) * KindFloat(0.333)

We’re dealing with 256 bits of precision when we’re dealing with constants of a kind, and when we now convert back to a variable, we’re moving that down to a 64 bit level of precision. There will be some precision loss, but, remember that floating points already are already not precise, IEEE754 binary decimals.

// Constant third will be of kind floating point.
const third = 1 / 3.0 // KindFloat(1) / KindFloat(3.0)

In the old days we used to call what we considered constants of a kind to be exact, they were like these very exact numbers because we had such high levels of precision that they were exact. So, we would look at third truly as 1/3 even though, eventually it turns to 56 bits but there be some precision loss.

// Constant zero will be of kind integer.
const zero = 1 / 3 // KindInt(1) / KindInt(3)

But then on above code, you could see that there’s no promotion going on. One is of kind int, three is of kind int, we do the division, we end up with zero, because that’s what’s that’s going to be, everything stays within the kind integer.

Promoto from Kind to Type

// This is an example of constant arithmetic between typed and
// untyped constants.
const one int8 = 1
const two = 2 * one // int8(2) * int8(1)

two ends up being a constant of type int8.

Parallel type system

const (
	// Max integer value on 64 bit architecture.
	maxInt = 9223372036854775807

	// Much larger value than int64.
	bigger = 9223372036854775808543522345

	// Will NOT compile
	// Compiler: "constant 9223372036854775808543522345 overflows int64"
	// biggerInt int64 = 9223372036854775808543522345
)

Practical use of constants

See the power of constants and their use in the standard library.

type Duration int64

const (
        Nanosecond  Duration = 1
        Microsecond          = 1000 * Nanosecond
        Millisecond          = 1000 * Microsecond
        Second               = 1000 * Millisecond
        Minute               = 60 * Second
        Hour                 = 60 * Minute
)

This is a second way to declare a type here in Go, what I would say is, the name type Duration is based, based on int64. This is not an alias, we have really two distinct named types here. We’re just using int64 as our base information or our base memory model for Duration. And I only want to do these types of things when the new type has its own representation and meaning, and it does here in the time package.

Duration represents time. Doesn’t represent an int64, it represents nanoseconds of time. We look at these constants because this is a real practical and clean way of how this idea of type and kind work together in constants.

Reasons why we can’t have enumerations in Go

We don’t want to create types as aliases to get compiler protection when they’re based on let’s say, those built-in types, which is where constants are allowed to be. Constants can only be based on the built-in types because again, they only exist at compile time.

iota

const (
    A2 = iota // 0 : Start at 0
    B2        // 1 : Increment by 1
    C2        // 2 : Increment by 1
)

We only have to assign iota one time to the very first constant in the block and we will automatically, for free, get the incremental.

iota is a very powerful mechanism if you’re creating a set of constants that are going to have some unique IDs and it just kind of let’s the language set all that up for you.

Wrap-up

  • Two types of constants, constants of a kind and constants of a type.
  • Literal values in Go are constants of a kind, they’re unnamed constants.
  • Constants of a kind can be implicitly converted by the compiler.
  • Constants of a kind can have up to 256 bits of precision.