Last Updated: May 22, 2026
Some values in a program should never change once the code is running. The maximum number of items you allow in a cart, the default tax rate, the currency code your store uses. Go has a dedicated way to express these: the const keyword. Constants look a lot like variables, but they come with stronger compile-time guarantees and a few useful quirks that make them more flexible than variables in expressions.
const Gives YouA constant is a named value that's fixed at compile time. Once declared, it cannot be changed, reassigned, or even have its address taken. The Go compiler bakes the value directly into the places that use it.
Why bother with constants instead of variables? Three practical reasons:
MaxItemsPerCart anywhere in your program, the code won't compile. You catch the mistake before shipping, not at 2 AM in production.if count > 50 is harder to maintain than if count > MaxItemsPerCart. The name explains why the limit exists, and changing the value happens in one place.Constants replace what would otherwise be called "magic numbers" scattered through your code. Anywhere you have a fixed business rule, a constant is the natural fit.
Cost: Constants are zero-cost at runtime. The compiler substitutes the literal value at every use site, so there's no memory allocation and no lookup overhead.
You declare a constant with const, followed by a name and a value. The syntax mirrors var but with one important difference: the value must be known at compile time.
You can declare constants at the package level (outside any function) or inside a function. Package-level constants are available throughout the file, and if their name starts with an uppercase letter, they're exported and available to other packages too.
A function-scoped constant like minOrder is fine when the value is only meaningful inside that one function. Most of your constants will live at the package level, though, because business rules tend to apply across the codebase.
When you have several related constants, declaring each one on its own line gets noisy. Go has a grouped form using parentheses, the same pattern you saw with import blocks.
The grouped form is the standard way to declare related constants. It makes them easy to find, easy to scan, and signals to anyone reading the code that these values belong together. Most Go projects group all the package-level constants into one or two const blocks near the top of the file.
You can declare a constant with an explicit type, just like a variable.
A typed constant behaves like a value of that specific type. MaxStock is an int, and you cannot use it where a float64 is expected without converting it first. Try this, and the compiler will reject it:
The error looks like:
Typed constants are useful when you want to lock down what a constant can be used for. They prevent accidental mixing of types, which matters when a value really does belong to one specific type and nothing else.
Here's where Go does something unusual. If you leave the type off a constant declaration, the constant is untyped. It still has a default kind (integer, float, string, bool, rune, or complex), but it can flex into different concrete types depending on where you use it.
The same Pi slot into both a float32 and a float64 without any explicit conversion. If Pi had been typed as float64, you'd have needed float32(Pi) for the first assignment. Untyped constants make code less cluttered with conversions.
This works because the compiler treats untyped constants as having ideal precision until they're committed to a concrete type. The same flexibility applies to integers:
The default kinds for untyped constants are:
| Constant literal | Default kind | Default type if assigned to a generic context |
|---|---|---|
42 | integer | int |
3.14 | float | float64 |
'A' | rune | int32 |
1 + 2i | complex | complex128 |
"hello" | string | string |
true, false | bool | bool |
The "default type" column matters when there's no specific context. If you write x := MaxItems with no type annotation, MaxItems commits to its default type (int here). But when you assign to a typed variable, the constant flexes to match.
The value on the right side of a const doesn't have to be a literal. It can be any expression Go can evaluate at compile time. That includes arithmetic, string concatenation, and references to other constants.
Every value here is computed by the compiler before the program ever runs. SecondsPerDay doesn't trigger any multiplication at runtime; it's just 86400 in the compiled binary. Same for the string concatenation. This is what "compile-time evaluated" means in practice.
What you can't do is use anything that requires runtime evaluation:
The rule is simple: if Go would need to actually execute code to figure out the value, it's not allowed in a const. Function calls (other than a few built-ins like len on a constant string), variable references, and runtime expressions are all out.
There's one nice exception: len on a string constant works because the length is known at compile time.
Constants and variables look similar, but they behave differently in two important ways.
You can't take a constant's address. Variables have memory addresses you can grab with &. Constants don't. The compiler bakes them in as literal values, so there's nothing to point at.
The error:
This makes sense once you understand what a constant really is. There's no MaxStock variable sitting in memory. Every place that uses MaxStock just has the literal 100 substituted in. There's nothing for a pointer to point at.
You can't change a constant at runtime. Variables can be reassigned. Constants cannot. Even within the function that declared them.
The error:
Here's a side-by-side summary:
| Feature | var | const |
|---|---|---|
| Value set at | Runtime or declaration | Compile time only |
| Can be reassigned | Yes | No |
Can take address (&) | Yes | No |
| Memory allocated | Yes | No, value substituted inline |
| Can hold a function result | Yes | No |
| Can hold a slice or map | Yes | No |
That last row is the next thing worth understanding.
Constants in Go can only hold values of basic types: numbers (integers, floats, complex), strings, booleans, and runes. You cannot have a constant slice, map, struct, or any other composite type.
The compiler responds with:
Same goes for maps:
And structs:
Why the restriction? Slices, maps, and structs all involve runtime memory allocation. A slice has a backing array. A map has internal hash table state. These can't be baked into the compiled binary as a single inline value the way 100 or "USD" can.
When you need fixed collections, use var with a package-level declaration:
This is a variable, not a constant. Code can technically reassign or mutate FeaturedCategories at runtime. If that bothers you, the convention is to keep such variables in an unexported form and expose them through a function that returns a copy.
Go's standard naming convention for constants is MixedCaps (also called PascalCase for exported names, camelCase for unexported). This is the same convention used for variables, functions, and types.
This is a Go convention, not a hard language rule. The compiler won't reject MAX_ITEMS_PER_CART or max_items_per_cart. But every well-written Go codebase uses MixedCaps, and tools like gofmt and golint assume it. Sticking to the convention makes your code feel idiomatic to other Go developers.
The capitalization of the first letter has actual meaning in Go. An uppercase first letter means the name is exported: it's visible to other packages that import yours. A lowercase first letter means it's unexported: visible only inside the package where it's defined.
This rule applies to every name in Go, not just constants. Choose uppercase when other packages need the value. Choose lowercase when it's an internal detail.
The program below uses several of the patterns covered so far. It calculates the final total for a cart, applying tax and free shipping rules from constants.
Every business rule that could change later (tax rate, shipping cost, free-shipping cutoff, currency code) lives in the const block at the top. Changing the tax rate is a one-line edit. The function logic stays the same. This is the everyday payoff of using constants well.