What is an untyped constant in Go, and what type does it take when assigned to a variable?
answer
- a kind, but not yet a type
- the compiler does the arithmetic
- exact, far wider than a machine word
- a fallback type when context gives none
- int, float64, rune, string, bool
basics
~20 sAn untyped constant has a kind - integer, float, rune, string, boolean - but no type, and the compiler evaluates it exactly. With no type available from context it takes its default type: int, float64, rune, string or bool.
solid answer
~50 s`const x = 5` declares an *untyped* constant: it has a kind (integer) but no type, and constant expressions built from it are evaluated exactly by the compiler rather than in machine words. Writing `const x int64 = 5` makes it *typed*, and the type sticks. An untyped constant acquires a type only when it must become a value at runtime, and when the surrounding code supplies none it falls back to its default type: `int` for integers, `float64` for floats, `rune` for rune literals, `string`, `bool`, `complex128`. That is why `x := 3.0` gives a `float64`. The arbitrary precision matters too: `const Big = 1 << 100` is legal and `Big >> 98` is 4, but `var n = Big` fails to compile because the value must fit in an `int`. Constant overflow is a compile error, never a wrap-around.
code
go · 6 linesconst Big = 1 << 100
var small = Big >> 98 // 4, an int
// var n = Big
// compile error: constant overflows intgo deeper
Know that x := 3 gives an int and x := 3.0 gives a float64, and that a constant declared without a type takes its type from where it is used.
Explain the kind-versus-type distinction, list the default types, and show that constant arithmetic is exact so overflow surfaces at compile time rather than as wrap-around.
Justify the constant's declared form in a package API: untyped for a plain limit that callers mix with their own integer types, typed when the type is part of the meaning.
Weigh what exported constants commit the team to, since their kind and type become part of a package's contract long before anyone thinks of them as API.
### Two kinds of constant Go has typed and untyped constants: ```go const a = 5 // untyped integer constant const b int64 = 5 // typed constant, type int64 ``` `b` is an `int64` everywhere it is used. `a` has a *kind* — integer — but no type at all until the moment it must become a value in a running program. This distinction has no counterpart in most languages and it is what makes Go's numeric literals feel flexible despite Go having no implicit numeric conversions. ### Arbitrary precision Constant expressions are evaluated by the compiler in exact arithmetic, not in the target machine's word size. The specification requires an implementation to represent integer constants with at least 256 bits, and floating-point constants with a mantissa of at least 256 bits. So this is legal: ```go const Big = 1 << 100 var small = Big >> 98 // 4 ``` The intermediate value is far wider than any Go integer type, but it never has to exist at runtime — only the final `4` does. Change the second line to `var n = Big` and the compiler reports that the constant overflows `int`. That is the rule to remember: an untyped constant may be as large as you like inside constant arithmetic, and is checked only when it is converted into a value of a concrete type. Constant overflow is always a compile-time error, never the silent wrap-around you get from arithmetic on variables. ### Default types When an untyped constant is used in a context that does not itself demand a type — a short variable declaration, a `var` with no type, or being passed as an `interface{}` argument — it takes its **default type**: | constant kind | default type | |---|---| | boolean | `bool` | | rune | `rune` (an alias for `int32`) | | integer | `int` | | floating-point | `float64` | | complex | `complex128` | | string | `string` | So `x := 3` is an `int`, `y := 3.0` is a `float64`, and `c := 'A'` is a `rune` holding 65 — not a `byte` and not a one-character string. ### What may be a constant at all Only the basic kinds above. There is no constant slice, map, struct, array or pointer, and a constant's expression must be computable at compile time, so `const now = time.Now()` is rejected: a function call is not a constant expression. When you want a fixed table, the options are a package-level `var` (mutable, so unexported and never handed out directly), a function that returns a fresh copy, or a `switch`/lookup written as code. ### Why untyped constants are the better default in a package Because an untyped constant adapts to the context it is used in, exporting `const MaxRetries = 5` is friendlier than `const MaxRetries int32 = 5`: callers can use it wherever an integer of any size is wanted without writing a conversion. Reach for a typed constant when the type is part of the meaning — for instance when the constants form an enum of a defined type and you *want* the compiler to reject a bare integer. ### Interaction with iota `iota` produces an untyped integer constant, so the same rules apply to enum blocks. `const ( StatusOpen = iota )` gives an untyped constant that will default to `int`; `const ( StatusOpen Status = iota )` gives a typed one, and that single word is the difference between an enum the compiler helps you with and a run of loose integers. ### Frequent misconceptions - "Untyped means dynamically typed." It does not. There is nothing untyped at runtime; the type is fixed at compile time, just not at the point of declaration. - "A large constant wraps around like a large `int`." It does not; it fails to compile the moment it must fit somewhere too small. - "`x := 3.0` gives a `float32` because the value is small." Size never influences the default type; the kind does. - "Constants are just read-only variables." They have no address, no memory and no runtime existence; you cannot take `&x` of a constant. ### How to answer Define untyped versus typed, give the default-type list, and then show the arbitrary-precision example — the `1 << 100` shift that compiles until you try to store it is the demonstration interviewers are hoping for, because it proves you understand that constant arithmetic happens in the compiler rather than on the machine.
- Why does `x := 'A'` give a rune rather than a byte or a string?A single-quoted literal is a rune constant, and the default type of a rune constant is `rune`, an alias for `int32`. Its value is the code point, 65. Double quotes would make it a string constant; a `byte` would require an explicit type, as in `var b byte = 'A'`.
- What can be declared const in Go, and what do you use for a fixed slice or map?Only booleans, runes, integers, floats, complex values and strings, and only from expressions the compiler can evaluate. There is no constant slice, map or struct. Use an unexported package-level `var` that nothing mutates, or a function that returns a fresh copy so callers cannot alter shared state.
- Is exporting an untyped constant from a package better than exporting a typed one?Usually, yes. An untyped `const MaxRetries = 5` adapts to whatever integer type the caller is using, with no conversion. Choose a typed constant when the type carries meaning — an enum of a defined type, for example — because there you want the compiler to reject a bare integer.
An untyped constant is like a measurement written on paper: it is an exact number until you have to pour it into a container, and only then does it matter whether the container is big enough.
saying these in an interview costs you the question
- Says untyped means dynamically typed at runtime
- Expects a huge constant to wrap around instead of failing to compile
- Thinks a small float constant defaults to float32
- Treats constants as read-only variables with addresses
- Believes a const slice or map can be declared