Variables and Data Types
Reviewed & published by Brayan K
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.
Part of the free Go course at LearnCodingFast — hands-on lessons with worked examples and the output they print, plus practice exercises and a quick quiz.
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
📊 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
}Your 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
}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
}3️⃣ 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
}4️⃣ 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
}
}Now 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
}Pro Tips
- 💡 Reach for := for local variables and var for package-level values or when you want an explicit type.
- 💡 Floats are float64 by default — prefer it over float32 unless 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: totalPrice tells a story; tp or x doesn'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 use var or const.
- "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 a var if 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 (with true/false lowercase)
- ✅ Every type has a zero value; multiple assignment lets you swap in one line
- ✅ const locks a value the compiler won't let you change
- ✅ Go has no implicit conversion — use int(x), float64(x), and strconv
- ✅ An unused local variable is a compile error, and := only works inside functions
Practice quiz
Which is the short variable declaration, valid only inside a function?
- var name = "Alice"
- var name string
- name := "Alice"
- let name = "Alice"
Answer: name := "Alice". The := form declares and assigns at once and works only inside functions.
What is the zero value of an int in Go?
- 0
- nil
- undefined
- -1
Answer: 0. Numbers default to 0; strings to ""; bools to false.
What is the zero value of a string?
- nil
- " "
- "null"
- "" (empty string)
Answer: "" (empty string). An unset string is the empty string, never undefined.
What is the zero value of a bool?
- true
- false
- 0
- nil
Answer: false. Bools default to false.
Why won't this compile: var x int with x never used?
- An unused local variable is a compile error
- x has no type
- int needs an initial value
- It compiles fine
Answer: An unused local variable is a compile error. Go treats an unused local variable as an error; assign to _ to discard.
How do you swap x and y in one line?
- swap(x, y)
- x = y; y = x
- x, y = y, x
- x, y := y, x
Answer: x, y = y, x. Multiple assignment swaps without a temp variable.
Which keyword declares a value that can never change?
- var
- const
- let
- final
Answer: const. const is fixed at compile time; the compiler rejects reassignment.
How do you convert an int total to a float64?
- total.(float64)
- float(total)
- (float64)total
- float64(total)
Answer: float64(total). The target type acts as a conversion function: float64(total).
Where does := fail to work?
- Inside main
- At package level (outside any function)
- Inside any function
- In a for loop
Answer: At package level (outside any function). At package level you must use var or const, not :=.
What does the blank identifier _ do?
- Declares a global
- Creates a constant
- Discards a value you intentionally ignore
- Marks a variable unused but kept
Answer: Discards a value you intentionally ignore. Assigning to _ discards a value, e.g. the error from strconv.Atoi.
Continue this course
- Previous: Introduction to Go
- Next: Data Types & Constants (iota) — Numeric types, strings, runes, constants and the iota enumerator