Short Variable Declarations
Understand the := short variable declaration operator in Go, its syntax, type inference, function-body restriction, redeclaration rules, and common pitfalls.
The := operator is Go’s compact way to declare and initialize a variable at the same time. It is called a short variable declaration. Under the hood it is equivalent to a var declaration with implicit type, but it saves you from writing var and the type name—the compiler figures out the type from the value on the right.
count := 10
message := "hello"
Both variables are fully declared and ready to use. The code above behaves exactly the same as:
var count = 10
var message = "hello"
The compiler infers int for count and string for message. You cannot add an explicit type to the left side of :=; the syntax does not allow it.
Syntax and Basic Usage
A short variable declaration follows the pattern:
identifierList := expressionList
Every identifier on the left must be paired with a value on the right. The right‑hand side can be a single expression, a comma‑separated list of expressions, or a function call that returns multiple values.
name, age := "Alice", 30
x, y := 10.5, 20.3
file, err := os.Open("data.txt")
In the last line, os.Open returns two values: a *File and an error. The := declares file as a *os.File and err as an error—both types are inferred from the function’s return signature. This is one of the most common uses of short declarations in Go, because many standard library functions return a result and an error together.
Initialization is mandatory:
Unlike var, the short declaration must provide an initial value for every variable. A line like x := is a syntax error. This is by design: := is meant for moments when you know the value right away.
Where Short Declarations Can Be Used
Short variable declarations work only inside functions. At the package level, every statement must begin with a keyword such as var, func, const, or import. The := operator is not a keyword, so the parser rejects it outside a function body.
package main
var x = 10 // ok: keyword var at package level
y := 20 // compile error: non-declaration statement outside function body
func main() {
z := 30 // ok: inside a function
}
This restriction shapes how Go programmers structure code. Package‑level state requires explicit var declarations, while function‑local variables benefit from the brevity of :=. It also means you cannot accidentally create a package‑level variable with a typo or shadowing through :=—the compiler will stop you.
Compile error outside functions:
Attempting to use := at package level produces:
non-declaration statement outside function body.
Only var (or const) can create package‑level variables.
Short declarations can also appear in the initializer of if, for, and switch statements. This is a convenient way to create variables that are scoped tightly to the statement.
if x := computeValue(); x > 100 {
fmt.Println("large value:", x)
}
// x is not visible here
The variable x exists only inside the if block and its else branches—a pattern you will see frequently when checking errors or processing results.
Redeclaration with := in the Same Scope
A unique rule makes := more flexible than a simple shorthand for var. In a multi‑variable short declaration, one or more of the variables on the left may already be declared in the same block, as long as at least one of the non‑blank variables is new. When this happens, := acts as an assignment for the existing variables and a declaration for the new one. The existing variables must keep the same type.
package main
import "fmt"
func main() {
a, b := 10, 20 // both a and b are new
fmt.Println(a, b) // 10 20
b, c := 30, 40 // b already exists, c is new → ok
fmt.Println(b, c) // 30 40
b, c = 50, 60 // both already declared → use =, not :=
fmt.Println(b, c) // 50 60
}
In the second line, b is redeclared—really reassigned—while c is introduced. The compiler checks that b was originally int and stays int. This rule is invaluable when dealing with error values: you often want to reuse an err variable while declaring a new result variable.
f, err := os.Open("first.txt")
// ...
g, err := os.Open("second.txt") // err reused, g is new
Without this rule, every call that returns an error would need a differently named error variable or an explicit var err error before the call, cluttering the code.
All variables must be of the same type as before:
If you try to redeclare a variable with a different type, the compiler will report a type mismatch. The existing variable’s type is fixed at its first declaration. For example, changing err from error to int via := is never allowed.
The redeclaration only works inside the same block. If you move to an inner block (created by if, for, etc.), := will create a new variable that shadows the outer one—this is normal variable shadowing, not the redeclaration rule covered here. The “same block” requirement means that consecutive lines at the same indentation level inside a function can take advantage of this behaviour.
Redeclaration works seamlessly with multi‑return functions:
If you see code like:
data, err := fetchData(); followed later by moreData, err := fetchMoreData();
and the compiler is happy, you are looking at the redeclaration rule in action. The programmer is reusing err correctly while introducing a new variable.
Comparing := and var Declarations
Although := and var both create variables, they differ in several important ways.
| Aspect | var declaration | := short declaration |
|---|---|---|
| Where allowed | Package level and function level | Function level only (including if, for, switch initializers) |
| Explicit type | Optional; can be specified | Not allowed; type always inferred |
| Initialization | Optional; missing value yields zero value | Mandatory; every variable must have a value |
| Syntax | var name type = value | name := value |
| Package‑level variables | Yes | No |
Choose var when:
- You are at package level.
- You need to declare a variable with a zero value and assign later.
- You want to make the type explicit for readability or documentation.
- You are grouping multiple declarations for a block.
Choose := when:
- You are inside a function and know the value immediately.
- You want shorter, more readable code.
- You are capturing multiple return values, especially the classic
value, err := ...pattern.
The two forms can coexist freely. A typical function might use var for a variable that accumulates results across a loop, and := for intermediate values.
func process(items []int) int {
var total int // zero value, accumulate later
for _, item := range items {
doubled := item * 2 // short declaration inside loop
total += doubled
}
return total
}
Common Mistakes and Misunderstandings
Even experienced Go developers can trip over these edge cases.
Trying to use := when no new variable exists. The compiler error no new variables on left side of := means every identifier on the left is already declared in the current block. You must use = instead.
x := 10
x := 20 // error: no new variables on left side of :=
x = 20 // correct
Believing := always creates a new variable. Because of the redeclaration rule, an existing name may be reassigned, not created. This is intentional and safe as long as the type remains the same.
Forgetting that := does not work at package level. This is especially confusing when moving code from a function to global scope during refactoring. The fix is to switch to var.
Shadowing with := in inner blocks. A short declaration inside an if or for block creates a brand‑new variable, even if an outer variable has the same name. The outer variable becomes invisible inside that block.
x := 1
if true {
x := 2 // new x, shadows outer x
fmt.Println(x) // 2
}
fmt.Println(x) // 1 — outer x unchanged
This is a common source of bugs. The redeclaration rule (same block) does not apply across block boundaries.
Using := when you meant to assign to the outer variable:
If you mean to update the outer variable, use = without :=, and declare the inner variable (if any) separately.
Example: var err error followed by assignment is clearer when you are not sure about re‑declaration.
Using := with a single underscore. The blank identifier _ can appear on the left side of :=, but it does not count as a “new variable” for the purpose of the redeclaration rule. If all non‑blank names are already declared, you will still get the “no new variables” error.
_, err := someFunc() // ok: err is new
_, err := otherFunc() // error: no new variables on left side of :=
To reuse err with a discarded first return, you need to ensure at least one other non‑blank identifier is new.
Summary
The short variable declaration is more than a syntactic sugar; the ability to redeclare existing variables in the same block, combined with the strict function‑body limitation, shapes how Go code is written. It nudges you toward small, self‑contained functions where local variables are declared and initialized in one motion, and where error values flow naturally without a parade of differently named err1, err2 variables.
The most important thing to carry forward: := is for inside functions, with values you have right now. When you need package‑level variables, zero‑value initialization, or explicit types, reach for var.