Constant expressions, part III

March 19, 2024

Did you miss yesterday’s Ask-me-anything session? You probably did. I had about 10-15 people there. But even with a small group, we had a ton of great questions! The Q&A session lasted about an hour, and covered topics such as book recommendations for going deeper into Go, what project to build to learn concurrency, and much more. Catch the replay on YouTube.


Let’s continue our discussion of constant expressions, with some more miscellaneous rules:

Constant expressions

The divisor of a constant division or remainder operation must not be zero:

3.14 / 0.0   // illegal: division by zero

I don’t think this needs any explanation.

The values of typed constants must always be accurately representable by values of the constant type. The following constant expressions are illegal:

uint(-1)     // -1 cannot be represented as a uint
int(3.14)    // 3.14 cannot be represented as an int
int64(Huge)  // 1267650600228229401496703205376 cannot be represented as an int64
Four * 300   // operand 300 cannot be represented as an int8 (type of Four)
Four * 100   // product 400 cannot be represented as an int8 (type of Four)

Recall that constants can be both typed and untyped:

const x = "foo"         // Untyped string constant
const y = string("foo") // Typed string constant

type MyString string

const z = MyString("foo") // Typed string constant

So the above rule is just the natural and obvious application of Go’s representability rules, as they apply to conversion between typed and untyped constants.

The mask used by the unary bitwise complement operator ^ matches the rule for non-constants: the mask is all 1s for unsigned constants and -1 for signed and untyped constants.

^1         // untyped integer constant, equal to -2
uint8(^1)  // illegal: same as uint8(-2), -2 cannot be represented as a uint8
^uint8(1)  // typed uint8 constant, same as 0xFF ^ uint8(1) = uint8(0xFE)
int8(^1)   // same as int8(-2)
^int8(1)   // same as -1 ^ int8(1) = -2

Nothing new to cover here. The same rules that apply to non-constants apply to constants, with regard to the ^ operator.

And one final note before we move beyond this topic, just re-iterating what I said yesterday about the optional rounding of some constants:

Implementation restriction: A compiler may use rounding while computing untyped floating-point or complex constant expressions; see the implementation restriction in the section on constants. This rounding may cause a floating-point constant expression to be invalid in an integer context, even if it would be integral when calculated using infinite precision, and vice versa.

Quotes from The Go Programming Language Specification Language version go1.22 (Feb 6, 2024)


Share this

Direct to your inbox, daily. I respect your privacy .

Unsure? Browse the archive .

Related Content


Constant expressions, continued

Today I’ll be answering your questions on my weekly live stream. Have a question about Go? About CI/CD? About my favorite pizza toppings? Join me! Or submit your question early and catch the replay, if you can’t join live. Today we continue the section on Constant expressions, with a grab bag of miscellaneous rules. Constant expressions … Applying the built-in function complex to untyped integer, rune, or floating-point constants yields an untyped complex constant.


Constant expressions

We made it through all the conversion rules! Let’s change gear and talk about… Constant expressions Constant expressions may contain only constant operands and are evaluated at compile time. Or framed differently: Any expression that contains non-constant operands, is not a constant expression. If you have a constant expression (e.g. 1 + 2) then extend it with a non-constant operand (e.g. 1 + 2 + userCount()), then it is no longer a constant expression.


Constant types

Long-time readers may recall that constants may be typed or untyped. Today we expore the details of how such constants are declared. Constant declarations … If the type is present, all constants take the type specified, and the expressions must be assignable to that type, which must not be a type parameter. If the type is omitted, the constants take the individual types of the corresponding expressions. If the expression values are untyped constants, the declared constants remain untyped and the constant identifiers denote the constant values.

Get daily content like this in your inbox!

Subscribe