Naming values
Every program works with values. Some of those values change as the program runs — a counter that increments, a status that flips from pending to complete. Others never change — a maximum retry count, the value of pi, the name of an application.
Go gives you two tools for naming values: variables for things that change, and constants for things that do not.
Variables
A variable is a named storage location. Its value can be reassigned at any point during the program's execution.
The var keyword
The most explicit way to declare a variable is with the var keyword, followed by the name, the type, and optionally an initial value:
var age int // declared with zero value: 0
var name string // zero value: ""
var active bool // zero value: false
var score int = 100 // declared with explicit value
When you declare a variable without assigning a value, Go initializes it to its zero value — the default value for that type. Every type has one: 0 for integers, 0.0 for floats, false for booleans, "" for strings. There is no such thing as an uninitialized variable in Go.
If you provide an initial value, Go can infer the type — you do not need to name it explicitly:
var age = 30 // inferred as int
var greeting = "Hello" // inferred as string
Inference uses the default type
When Go infers a type from an untyped literal, it uses the default type for that kind of value. A bare integer like 42 defaults to int. A bare float like 3.14 defaults to float64. If you need a specific type, declare it explicitly: var x float32 = 3.14.
Short variable declaration
Inside a function, you can omit both var and the type using the := operator. It declares and initializes the variable in one step:
age := 30
greeting := "Hello"
isActive := true
The type is always inferred from the right-hand side. Short declarations are the most common form of variable declaration in Go — they are concise and keep functions readable.
One important restriction: := cannot be used at package level. It is only valid inside functions.
package main
counter := 0 // compile error: not inside a function
func main() {
counter := 0 // fine
}
Reassigning and reusing :=
:= requires that at least one variable on the left side is new. This allows you to reuse it when declaring a new variable alongside an existing one:
x := 10
x, y := 20, 30 // valid: y is new, x is reassigned
If all variables on the left already exist, use plain = for reassignment:
x := 10
x = 20 // reassignment, not a new declaration
Multiple variables
You can declare multiple variables on a single line:
var x, y, z int = 1, 2, 3
a, b := "hello", true
Or group them inside a var block — common for related declarations at the top of a function or at package level:
var (
host = "localhost"
port = 8080
timeout = 30
)
Package-level vs function-level
Variables can be declared at two scopes:
Package-level variables are declared outside any function. They are accessible throughout the entire package and initialized before main runs.
Function-level variables are declared inside a function. They exist only for the duration of that function call and are invisible outside it.
package main
var serverHost = "localhost" // package-level
func connect() {
port := 5432 // function-level
fmt.Println(serverHost, port)
}
Unused variables are a compile error
Go refuses to compile a program that declares a local variable without using it. This is intentional — it prevents dead code and keeps functions clean. Package-level variables are exempt from this rule.
Constants
A constant is a named value that cannot change after it is set. It is not a variable with a guard — it is evaluated entirely at compile time and never occupies a memory location at runtime.
The const keyword
Constants are declared with const:
const maxRetries = 3
const appName = "MyApp"
const pi = 3.14159265358979
Like variables, constants support grouped declarations:
const (
maxRetries = 3
timeout = 30
appName = "MyApp"
)
What can be a constant
Not every value qualifies as a constant. The compiler must be able to determine its value before the program runs. Valid constant values include:
- Boolean literals:
true,false - Integer, float, and complex literals
- String and rune literals
- Expressions that can be evaluated at compile time:
1 << 8,2 * 3.14,1 < 2 - Calls to certain built-in functions:
len,cap,real,imag,complex
const bufferSize = 1 << 8 // 256
const isDebug = false
const greeting = "Hello"
const alwaysTrue = 1 < 2 // evaluated at compile time — true
Function calls that produce runtime values — like reading from a file or calling time.Now() — cannot be used as constants.
Typed and untyped constants
Constants in Go come in two forms: typed and untyped.
A typed constant carries an explicit type and can only be directly assigned to a variable of that same type:
const maxRetries int = 3
var x int = maxRetries // fine
var y int64 = maxRetries // compile error: cannot use maxRetries as int64
An untyped constant has no fixed type. It carries a kind (integer, float, string, or boolean) and adapts to whatever type is expected at the point of use:
const maxRetries = 3 // untyped integer constant
var x int = maxRetries // fine
var y int64 = maxRetries // fine — adapts to int64
var z float64 = maxRetries // fine — adapts to float64
Prefer untyped constants
Leave constants untyped unless you have a specific reason to restrict them. Untyped constants are more flexible — they work wherever a compatible kind is expected, without explicit conversion.
iota
When you need a sequence of related integer constants, Go provides iota — a counter that starts at zero and increments by one with each entry in the block:
const (
Sunday = iota // 0
Monday // 1
Tuesday // 2
Wednesday // 3
Thursday // 4
Friday // 5
Saturday // 6
)
iota resets to zero at the start of every new const block. It is the idiomatic Go way to define enumerations.
Variables vs constants
Both variables and constants can be declared at package or function level, and both support grouped declarations with braces. The differences are more significant than they might appear:
var | const | |
|---|---|---|
| Value can change | yes | no |
| Evaluated at | runtime | compile time |
Short form (:=) | yes (functions only) | no |
| Can remain unused | no (local only) | yes |
The unused rule is worth emphasizing: a local variable that is declared but never referenced is a compile error. Constants are always exempt — they can exist purely for documentation or future use without the compiler objecting.