Lesson 2 • Beginner
Variables and Data Types 🐹
By the end of this lesson you'll be able to store text, numbers, and true/false values in Go, declare them three different ways, convert safely between types, and print them in clean output — the foundation of every Go program you'll write.
What You'll Learn in This Lesson
- Declare variables with var and an explicit type
- Use the short declaration := inside functions
- Work with Go's basic types: int, float64, string, bool
- Lock values that never change with const
- Rely on zero values and use multiple assignment
- Convert between types with int(x), float64(x), and strconv
package main program that prints with fmt.Println. Every code block below is genuine Go: paste it into the free Go Playground (no install needed) and check it against the expected output.string) holds text; a measuring cup labelled "Cups" (int) holds whole numbers; a scale labelled "Weight" (float64) holds decimals. Each container only holds one kind of thing — you can't pour flour into a liquid measuring cup. Go's type system works exactly like that: every variable has a fixed type, and to move a value between containers you must explicitly re-label it (that's type conversion). That strictness is a feature — it catches mistakes before your program ever runs.📊 The Core Data Types
| Type | Holds | Example | Zero value |
|---|---|---|---|
| int | Whole numbers | age := 25 | 0 |
| float64 | Decimal numbers | h := 1.75 | 0 |
| string | Text | n := "Al" | "" (empty) |
| bool | true / false | ok := true | false |
Strings use "double quotes". Go also has byte, rune, and sized numbers like int64, but the four above carry you through almost everything early on. Note Go writes true/false in lowercase.
1️⃣ Declaring Variables
Go gives you three ways to make a variable. var name type = value is the explicit form. You can drop the type and let Go infer it from the value. And inside a function the short declaration := does both at once — it's the form you'll reach for most. Use descriptive names like firstName or totalPrice; code is read far more often than written. Read this worked example, run it, then you'll write your own.
package main
import "fmt"
func main() {
// A variable is a named box that holds ONE type of value.
// Method 1: var with an explicit type.
// pattern -> var name type = value
var name string = "Alice" // text -> always in "double quotes"
var age int = 28 // whole number (no decimal point)
var height float64 = 5.7 // decimal number
var isActive bool = true // true or false (lowercase in Go)
// Method 2: var with type inference — Go reads the type from the value.
var city = "London" // city is a string
var score = 95 // score is an int
// Method 3: short declaration := (most common, FUNCTIONS ONLY).
// No "var", no type — Go infers both. Declares AND assigns at once.
pi := 3.14159 // pi is a float64
fmt.Println(name, age, height, isActive) // Alice 28 5.7 true
fmt.Println(city, score) // London 95
fmt.Printf("pi = %.2f\n", pi) // pi = 3.14
}Alice 28 5.7 true
London 95
pi = 3.14Your turn. The program below is almost complete — fill in the three blanks marked ___ using the hints, then run it and check your output.
package main
import "fmt"
func main() {
// 🎯 YOUR TURN — replace each ___ then run it.
// 1) Make an int called "year" set to the current year
var year int = ___ // 👉 a whole number, e.g. 2026
// 2) Make a string called "city" using short declaration :=
city := ___ // 👉 text in "double quotes"
// 3) Make a float64 called "price" set to 9.99
price := ___ // 👉 a decimal number
// These lines already work once your variables exist:
fmt.Printf("In %d, a coffee in %s costs £%.2f\n", year, city, price)
// ✅ Expected output (example):
// In 2026, a coffee in London costs £9.99
}In 2026, a coffee in London costs £9.99___ blanks, then run it free at the Go Playground and compare with the expected output.2️⃣ Zero Values & Multiple Assignment
In some languages a variable with no value is "undefined" and reading it is a bug. Go is safer: every type has a zero value it falls back to — 0 for numbers, "" for strings, false for bools. You can also declare several variables at once, which makes the classic two-line swap a single, clean line.
package main
import "fmt"
func main() {
// ZERO VALUES — a var with no value is NEVER "undefined".
// Go gives every type a sensible default.
var i int // 0
var f float64 // 0
var s string // "" (empty string)
var b bool // false
fmt.Printf("int=%d float=%.1f str=%q bool=%t\n", i, f, s, b)
// MULTIPLE ASSIGNMENT — declare several at once on one line.
x, y := 10, 20
fmt.Println("x =", x, "y =", y) // x = 10 y = 20
// Swap two values in a single line — no temp variable needed.
x, y = y, x
fmt.Println("after swap:", x, y) // after swap: 20 10
}int=0 float=0.0 str="" bool=false
x = 10 y = 20
after swap: 20 103️⃣ Constants with const
A constant is a value that must never change after it's set, and the Go compiler enforces that for you. Use const for fixed facts like a tax rate or a maximum limit — it documents your intent and stops accidental edits. Constants can live at package level (outside any function) or inside one. Note constants use =, never :=.
package main
import "fmt"
// Constants are usually declared at package level with const.
// A const NEVER changes after it's set — the compiler enforces that.
const MaxUsers = 1000
const Pi = 3.14159
func main() {
// const also works inside a function.
const greeting = "Hello"
fmt.Println(greeting + ", Go!") // Hello, Go!
fmt.Println("MaxUsers:", MaxUsers) // MaxUsers: 1000
fmt.Printf("Pi = %.2f\n", Pi) // Pi = 3.14
// MaxUsers = 2000 // ❌ would NOT compile: cannot assign to const
}Hello, Go!
MaxUsers: 1000
Pi = 3.144️⃣ Type Conversion
Go is statically typed and — unlike JavaScript or Python — has no implicit conversion. You can't add an int to a float64 directly; you convert one first. The syntax is the target type as a function: float64(x), int(x). Converting between numbers and text is different — that needs the strconv package (strconv.Itoa for int→string, strconv.Atoi for string→int).
package main
import (
"fmt"
"strconv"
)
func main() {
// Go has NO implicit conversion. You MUST convert types yourself.
// int -> float64, so you can divide without losing the decimals.
total := 7
count := 2
average := float64(total) / float64(count)
fmt.Printf("average = %.1f\n", average) // average = 3.5
// float64 -> int truncates (drops the decimals, no rounding).
price := 9.99
whole := int(price)
fmt.Println("whole pounds:", whole) // whole pounds: 9
// Numbers <-> text need the strconv package.
n := 42
asText := strconv.Itoa(n) // int -> string "42"
fmt.Printf("as text: %q\n", asText) // as text: "42"
back, err := strconv.Atoi("100") // string -> int
if err != nil { // Atoi returns an error if it can't
fmt.Println("not a number")
} else {
fmt.Println("parsed:", back+1) // parsed: 101
}
}average = 3.5
whole pounds: 9
as text: "42"
parsed: 101Now you try. Input from a user always arrives as string text, so before you can do maths you must convert it. Fill in the two blanks:
package main
import (
"fmt"
"strconv"
)
func main() {
// 🎯 YOUR TURN — convert these values, then run it.
quantityText := "3"
var unitPrice float64 = 4.50
// 1) Convert quantityText (a string) to an int
quantity, _ := ___ // 👉 strconv.Atoi(quantityText)
// 2) Convert quantity (an int) to a float64 so the maths works
total := ___ * unitPrice // 👉 float64(quantity)
fmt.Printf("%d items x £%.2f = £%.2f\n", quantity, unitPrice, total)
// ✅ Expected output: 3 items x £4.50 = £13.50
}3 items x £4.50 = £13.50Pro Tips
- 💡 Reach for
:=for local variables andvarfor package-level values or when you want an explicit type. - 💡 Floats are
float64by default — prefer it overfloat32unless you have a specific reason. - 💡 Always check the error from
strconv.Atoi— text from a user might not be a number. Discard it with_only when you're certain. - 💡 Name things well:
totalPricetells a story;tporxdoesn't.
Common Errors (and the fix)
- "declared and not used" — in Go an unused local variable is a compile error, not a warning. Use the variable, remove it, or assign it to
_(the blank identifier) to discard it on purpose. - "non-declaration statement outside function body" —
:=only works inside a function. At package level you must usevarorconst. - "invalid operation: mismatched types int and float64" — Go never mixes types implicitly. Convert one side first:
float64(myInt) + myFloat. - "no new variables on left side of :=" — you used
:=on a name that already exists. Use=to reassign an existing variable. - "cannot assign to MaxUsers (neither addressable nor a map index expression)" — you tried to change a
const. Constants can't be reassigned; use avarif the value needs to change.
📋 Quick Reference
| Task | Go Syntax | Result |
|---|---|---|
| Short declare | name := "Alice" | string |
| Explicit type | var age int = 25 | int |
| Constant | const Pi = 3.14 | never changes |
| int → float64 | float64(7) | 7.0 |
| float64 → int | int(3.9) | 3 (truncates) |
| int → string | strconv.Itoa(42) | "42" |
| string → int | strconv.Atoi("42") | 42, nil |
Zero values: int → 0, float64 → 0, string → "" (empty), bool → false.
Frequently Asked Questions
Q: When should I use := versus var?
Use := for the everyday case — a local variable inside a function whose type is obvious from its value. Use var when you need a package-level variable, want to state the type explicitly, or want a variable to start at its zero value with no initial value.
Q: Why won't my program compile because of an "unused" variable?
Go treats an unused local variable as a real error to keep code clean. Either use it, delete it, or assign it to the blank identifier _ if you genuinely need to ignore a value (common with the error from strconv.Atoi).
Q: Why can't I add an int to a float64?
Go never converts number types behind your back, because silent conversions hide bugs. Convert one side explicitly: float64(myInt) + myFloat. This is intentional strictness, not a missing feature.
Q: What's the difference between const and var?
A var can be reassigned later; a const is fixed at compile time and can never change. Use const for fixed facts (a tax rate, a maximum) so the compiler stops accidental edits.
Mini-Challenge: Profile Card
No blanks this time — just a brief and an outline to keep you on track. Declare the variables yourself, print a tidy profile, and check your output against the example in the comments. This is exactly the kind of small program real apps are made of.
package main
import "fmt"
func main() {
// 🎯 MINI-CHALLENGE: Profile card
// 1. Declare: name (string), age (int), heightM (float64),
// likesGo (bool). Use := where you can.
// 2. Print a tidy 4-line profile with fmt.Printf and verbs
// %s (string), %d (int), %.1f (1-decimal float), %t (bool).
// 3. BONUS: it's their birthday — add 1 to age, then print
// "Next year you'll be 21".
//
// ✅ Example output:
// Name: Sam
// Age: 20
// Height: 1.8m
// Likes Go: true
// Next year you'll be 21
// your code here
}🎉 Lesson Complete!
- ✅ Three ways to declare:
var name type = value, type inference, and:= - ✅ Core types:
int,float64,string,bool(withtrue/falselowercase) - ✅ Every type has a zero value; multiple assignment lets you swap in one line
- ✅
constlocks a value the compiler won't let you change - ✅ Go has no implicit conversion — use
int(x),float64(x), andstrconv - ✅ An unused local variable is a compile error, and
:=only works inside functions - ✅ Next lesson: Functions and Methods — package up logic you can reuse
Sign up for free to track which lessons you've completed and get learning reminders.