Recommended resources
The freely available resources this lesson will direct you to.
Hello! In the previous lesson, you created a Go module and used the toolchain to run, check, and test it. Now we will look more closely at the values inside that program: how to declare variables and constants, what an uninitialized variable contains, and when Go requires an explicit conversion.
Allow about 40 minutes, including time to update and run your greeter module. You can keep its existing test throughout this lesson.
Declarations and assignments
A declaration introduces a name and gives it a type. An assignment changes the value held by an existing variable. Go offers several ways to declare a variable:
var count int // Declared without an initial value.
var language = "Go" // Type inferred from the initial value.
name := "visitor" // Short declaration; usable only inside a function.
The last two forms still produce statically typed variables. Inference means Go determines the type at declaration time, not that the variable can later change type.
Read the two paragraphs under “Short variable declarations” on page 10/17 of the official A Tour of Go. Then look at the runnable example directly below them, especially lines 6–10: it places a var declaration beside two := declarations.
The official Go tour gives the scope rule for the short declaration and shows what it looks like beside var.
In “Short variable declarations,” read the two paragraphs, beginning with “Inside a function” and ending with the explanation of why the construct is unavailable outside functions. Then inspect the example immediately below, from var i, j int = 1, 2 through its fmt.Println call. Notice that := appears inside main, not at package level.
Use var at package level, or when you want to declare a variable before giving it a value. Inside a function, := is often convenient when you have an initial value. Once a variable exists, use = to assign to it:
count := 1
count = 2
Writing count := 2 again in the same block, with no other new variable on the left, is not an assignment; it is an invalid short declaration. Go also rejects a local variable that you declare but never use. You need not use it immediately, but it must be used somewhere in that function.
Watch John Elder demonstrate short declarations and variables without initial values. His example also encounters the unused-local-variable error.
Variables and Constants In Go - Learn Golang #2
In Variables and Constants In Go - Learn Golang #2, John Elder demonstrates :=, then declares variables whose values will be supplied later.
Watch short declarations for type inference and the function-body restriction. Continue with uninitialized variables; focus on the values printed before and after assignment. When the video encounters an unused-variable error, remember that a local variable must be used somewhere in its function, not necessarily right after declaration.
Zero values are real values
A variable declared without an initializer does not contain an unpredictable value. Go gives it the zero value of its type:
var attempts int // 0
var fraction float64 // 0
var enabled bool // false
var message string // ""
You can read or print these immediately. For example, fmt.Printf("%q", message) prints "", making the empty string visible. Later, an ordinary assignment can replace any of these values.
This rule is useful, but a zero value is not automatically a valid business value. A guest count of 0 may mean “none”; in a different program, it might mean “not supplied yet.” Go establishes the value, while your application decides its meaning. Other kinds of values, including slices, maps, and pointers, have nil zero values; later lessons will examine what that means for each of them.
Constants describe values that cannot be reassigned
Declare a constant with const and an initial value:
const serviceName = "greeter"
const maxGuests = 4
Unlike a variable, a constant cannot be assigned a new value later. Constants can also be grouped:
const (
serviceName = "greeter"
maxGuests = 4
)
Go constants can be untyped or typed. Both constants above are untyped: Go can use maxGuests in a compatible numeric context if its value fits. You can specify a type when you need one:
const fixedLimit int = 4
A typed int constant is not silently treated as a float64 variable. If that is what you need, write float64(fixedLimit). Constants are for values Go can determine at compile time; a value obtained from user input must instead be held in a variable.
Use descriptive Go-style names such as maxGuests, rather than assuming constants must be written in all capitals. The spelling does not make a name constant; the const declaration does.
Conversion is explicit—and parsing is different
Go does not silently mix variables of different numeric types. If an integer count must participate in floating-point division, convert it deliberately:
count := 7
fmt.Println(count / 2) // 3: integer division
fmt.Println(float64(count) / 2) // 3.5: floating-point division
Placement matters: float64(count / 2) would produce 3, because the integer division has already happened before conversion. Converting a floating-point variable to int discards its fractional part:
price := 3.9
fmt.Println(int(price)) // 3
A conversion changes the type of a value; it does not edit the original variable. Some conversions can lose information, so their presence in code is worth noticing.
Text containing digits needs a different operation. string(65) produces the character "A", not "65". To format an integer as decimal text, use strconv.Itoa(65). To interpret decimal text as an integer, use strconv.Atoi("65"), which returns both a number and an error. The error matters because not every string is a valid integer.
Watch the type-mismatch and parsing portion of Hitesh Choudhary’s Conversions in golang. The video uses strconv.ParseFloat rather than Atoi, but the important distinction is the same: parsing text can fail, unlike a straightforward numeric conversion.
Conversions in golang by Hitesh Choudhary shows why numeric-looking input is still a string and introduces a standard-library parsing function.
Watch the type mismatch when the presenter tries to add a number to string input. Then watch parsing text with strconv.ParseFloat, focusing on why the function supplies both a result and an error. We will use strconv.Atoi below because our example needs an integer rather than a floating-point number.
Apply the ideas in your module
Replace main.go in your greeter module with the following program. It preserves greet, so the test from the previous lesson should still work.
package main
import (
"fmt"
"strconv"
)
const (
serviceName = "greeter"
maxGuests = 4
)
func greet(name string) string {
return "Hello, " + name + "!"
}
func main() {
var accepted int
var active bool
var label string
fmt.Printf("initial: accepted=%d active=%t label=%q\n",
accepted, active, label)
guest := "Go"
accepted = 1
fmt.Println(greet(guest))
fmt.Printf("%s: %d/%d active=%t\n",
serviceName, accepted, maxGuests, active)
ratio := float64(accepted) / maxGuests
fmt.Printf("occupancy: %.2f\n", ratio)
text := "2"
guests, err := strconv.Atoi(text)
if err != nil {
fmt.Println("invalid guest count:", err)
return
}
fmt.Println("after parsing:", accepted+guests)
}
Here accepted, active, and label show three zero values. guest := "Go" declares a new local variable, while accepted = 1 assigns to an existing one. In the ratio, accepted needs an explicit conversion because it is an int variable; the untyped constant maxGuests can be used with the float64 value. Finally, Atoi checks whether text can actually be read as an integer before the program adds it to accepted.
Run your familiar checks:
go fmt ./...
go vet ./...
go test ./...
go run .
The final line of output should be after parsing: 3. If you change text to "two", the program should report a parsing error instead of proceeding with a number. That is why checking err is part of using a parser correctly.
Takeaways
Use var when a declaration needs an explicit type or no initial value, and := for a new variable inside a function when its initial value determines the type. Uninitialized variables receive type-specific zero values. Constants cannot be reassigned. For different numeric types, make the conversion visible; for numeric text, use a parser and check its error.
Next, you will compare arrays, slices, and maps. Their different zero values and assignment behavior will make the distinctions from this lesson especially useful.