Variables
A variable is a named place to store a value. Every Go variable has a fixed type, chosen when it's declared — either written out by you or worked out by the compiler from the starting value. This lesson covers the two ways to declare variables, zero values, declaring several at once, scope and shadowing.
Declaring with var
The full form is var name type = value. You can leave out the type when there's a value (Go infers it), or leave out the value when there's a type (the variable starts at its zero value):
package main import "fmt" func main() { var city string = "Lisbon" var population = 545_000 // type inferred: int var rainfall float64 // no value: starts at 0 fmt.Println(city, population, rainfall) rainfall = 774.2 // assign a new value with = population += 1000 fmt.Println(city, population, rainfall) }
Lisbon 545000 0 Lisbon 546000 774.2
Underscores inside a number, as in 545_000, are just for readability.
Short declarations with :=
Inside functions, the short form name := value declares a variable and infers its type. It's what Go programmers use most of the time:
package main import "fmt" func main() { name := "Ada" age := 36 height := 1.65 admin := false fmt.Printf("%s is %d, %.2f m tall, admin: %t\n", name, age, height, admin) fmt.Printf("types: %T %T %T %T\n", name, age, height, admin) }
Ada is 36, 1.65 m tall, admin: false types: string int float64 bool
The %T verb prints a value's type. Whole-number literals become int and numbers with a decimal point become float64.
Remember the difference: := declares a new variable, = assigns to an existing one. Using := twice for the same name in the same scope is an error, and so is = with a name that hasn't been declared:
package main import "fmt" func main() { count := 1 count := 2 total = 10 fmt.Println(count) }
./main.go:7:8: no new variables on left side of := ./main.go:8:2: undefined: total
:= only works inside functions. At package level, outside any function, you must use var.
Zero values
Go has no uninitialised variables. A variable declared without a value holds its type's zero value:
package main import "fmt" func main() { var i int var f float64 var s string var b bool var p *int var list []string fmt.Println(i, f, b) fmt.Printf("%q\n", s) fmt.Println(p == nil, list == nil, len(list)) }
0 0 false "" true true 0
| Type | Zero value |
|---|---|
| Numbers | 0 |
string | "" (empty string) |
bool | false |
| Pointers, slices, maps, channels, functions, interfaces | nil |
| Arrays and structs | Every element or field set to its own zero value |
Well-designed Go types are useful at their zero value: an empty string, a zero count, or a nil slice you can append to straight away.
Several variables at once
package main import "fmt" var ( // a group of package-level variables appName = "Inventory" version = 3 ) func main() { x, y := 10, 20 var width, height int = 640, 480 first, last := "Grace", "Hopper" x, y = y, x // swap in one statement fmt.Println(appName, version) fmt.Println(x, y, width, height, first, last) }
Inventory 3 20 10 640 480 Grace Hopper
In a multiple assignment, all the values on the right are evaluated before any variable is changed, which is why x, y = y, x swaps the two without a temporary variable.
:= with several names is allowed as long as at least one of them is new; the others are simply assigned. You'll see this constantly with errors:
package main import ( "fmt" "strconv" ) func main() { a, err := strconv.Atoi("42") b, err := strconv.Atoi("x7") // b is new, err is reused fmt.Println(a, b, err) }
42 0 strconv.Atoi: parsing "x7": invalid syntax
Scope and shadowing
A variable exists from its declaration to the end of the block it was declared in. A variable declared in an inner block with the same name as an outer one shadows it — the inner one is a separate variable:
package main import "fmt" func main() { level := "outer" if true { level := "inner" // a new variable, only inside this block fmt.Println(level) } fmt.Println(level) count := 0 if true { count = 5 // plain = changes the outer variable } fmt.Println(count) }
inner outer 5
Accidental shadowing — typing := where you meant = inside an if — is a classic Go bug. If a variable mysteriously keeps its old value after a block, check for a stray colon.
The blank identifier
Every declared local variable must be used. When a function returns a value you don't need, assign it to _:
package main import ( "fmt" "strconv" ) func main() { n, _ := strconv.Atoi("123") // ignore the error (only when you're sure it can't fail) for _, word := range []string{"red", "green"} { fmt.Println(n, word) } }
123 red 123 green
Try it yourself
Declare variables for a product and print a line describing it. Then try declaring a variable you never use and read the compiler's message.
Desk lamp: 34.90 (12 in stock, on sale: false)