Values, types and zero values
Making variables two ways, the small set of built-in types, the value every variable starts with, and why Go never converts a type behind your back.
Two ways to make a variable
Inside a function, name := "Go" declares and assigns in one move — the type comes from the value on the right. The longer form, var year int = 2009, is used when you want the type written down, or when the value has to be declared before it is known.
A variable you declare and never use is a compile error, the same as an unused import. The rule sounds fussy until you have read code where a value was assigned and forgotten; in Go, that mistake does not exist.
The types worth knowing
Whole numbers are int, which is the width that suits the machine — 64 bits almost everywhere you will run Go. Decimals are float64. Text is string. Truth is bool. A single byte of text is byte; a single character is rune, which holds a whole Unicode code point rather than one byte of its encoding.
A declared variable that is not assigned gets its zero value: 0 for numbers, false for booleans, "" for strings, and nil for pointers, slices and maps. No value in Go is ever undefined — a fresh variable is always usable, just empty.
Conversions are always written out
Go has no implicit numeric conversion. Adding an int to a float64 is a compile error, and the fix is explicit: float64(count) / 2.0. The same for text: strconv.Itoa(42) turns a number into a string, and each conversion is a line you can see.
Constants are the exception in the useful direction — const limit = 10 fits wherever a number of any suitable type is needed, because an untyped constant takes its type from the context. Everything else is spelled out, which is why reading Go tells you the types without running anything.
A named type is a new type with the same underlying representation: type Celsius float64 is not a float64, and adding one to a plain float needs a conversion. That sounds pedantic until a function takes both Celsius and Fahrenheit — the compiler then refuses the mix-up that a bare float64 would have accepted in silence. The conversion is where you say which scale you mean.
One value, converted
Follow one value through a conversion. count := 7 makes an int holding seven. float64(count) reads those seven units and produces a new float64 holding 7.0; count is still an int, unchanged. Divide by 2.0 and the result is 3.5, because both operands are now floating point. Write count / 2 instead and the answer is 3: the fraction is discarded at the division, not at the print. The same rule explains int(19.99), which is 19, and int(-19.99), which is -19 — conversion truncates toward zero rather than rounding.
Declared, converted, formatted
package main
import (
"fmt"
"strconv"
)
const limit = 10
func main() {
var visits int // zero value: 0
visits = visits + 1
name := "Ada"
price := 19.5
discount := float64(limit) / 100.0 * price
fmt.Println(name, "visited", visits, "time(s)")
fmt.Printf("price %.2f, with discount %.2f\n", price, price - discount)
fmt.Println("as text: " + strconv.Itoa(visits))
}A converter that says what it does
Run it, then add a second temperature of your own and print both with one decimal place. Try removing the float64 conversion and read what the compiler says.
Do not reach for a conversion to silence the compiler. When Go refuses to mix two types it is asking which meaning you want — the exact answer is always a written conversion, never a guess.
What the type checker is doing
Every expression has a type before the program runs, and the compiler works it out from the inside out: a literal has one, an operator has rules about which types it accepts, and a variable keeps the type it was declared with for its whole life. Nothing changes type at run time.
That is why count := 3 makes an int and stays one. float64(count) is a conversion, not a promotion: it produces a new value of another type and leaves the original alone. Conversions between numbers can lose information — a fraction dropped by int, a large integer rounded by float64 — which is exactly why Go makes you write them.
The zero value is part of the same design. New memory is cleared, and every type gives those zero bytes a meaning: 0, false, "", nil. A variable is never a trap waiting for an assignment; it is empty, and empty is defined.
Three traps live in this chapter. Integer division throws the fraction away — 7 / 2 is 3, not 3.5. Binary floating point cannot store most decimals exactly, so two float64 variables holding 0.1 and 0.2 do not sum to exactly 0.3; compare with a tolerance, not with ==. And := inside an inner block declares a new variable, leaving the outer one untouched.
Try these next
- Divide two integers, then convert one operand with
float64, and print both answers using the same numbers. - Store
math.MaxInt64in a variable, add one, and print the result. Then printmath.MaxInt32and explain the difference in width. - Declare
var total float64, add anintto it, and remove the conversion to read the compiler error.
A budget with written conversions
Run it, then add a fourth price and explain why the printed total is lower than adding the prices as decimals. Then change the budget and the share.
Check what you learned
One question at a time. A wrong answer explains itself — take another go.
Part 1 of 5
What is the value of a freshly declared var count int?
