In the previous tutorial, you installed Go, set up VS Code, and wrote your first program. Now it is time to learn about variables, types, and constants. These are the building blocks of every Go program.
Declaring Variables
Go has two ways to declare variables: the var keyword and the short declaration :=.
The var Keyword
The var keyword declares a variable with an explicit type:
package main
import "fmt"
func main() {
var name string = "Alex"
var age int = 25
var height float64 = 1.75
var isStudent bool = true
fmt.Println(name, age, height, isStudent)
}
Output:
Alex 25 1.75 true
You can also let Go figure out the type from the value:
var name = "Alex" // Go knows this is a string
var age = 25 // Go knows this is an int
var height = 1.75 // Go knows this is a float64
var isStudent = true // Go knows this is a bool
You can declare multiple variables at once:
var (
firstName string = "Alex"
lastName string = "Smith"
age int = 25
)
Short Declaration :=
Inside functions, you can use := to declare and assign a variable in one step:
package main
import "fmt"
func main() {
name := "Alex" // Same as: var name string = "Alex"
age := 25 // Same as: var age int = 25
height := 1.75 // Same as: var height float64 = 1.75
isStudent := true // Same as: var isStudent bool = true
fmt.Println(name, age, height, isStudent)
}
The := operator is the most common way to declare variables in Go. Most Go code uses := inside functions.
Important: You cannot use := outside of functions. For package-level variables, you must use var:
package main
// Package-level variable — must use var
var appName = "MyApp"
func main() {
// Inside a function — can use :=
version := "1.0"
fmt.Println(appName, version)
}
var vs :=
When should you use var and when should you use :=?
Use var when… | Use := when… |
|---|---|
| Declaring package-level variables | Declaring variables inside functions |
| You want to declare without a value | You want to declare and assign at the same time |
| You want to be explicit about the type | You want Go to figure out the type |
In practice, most Go developers use := inside functions and var only when needed.
Basic Types
Go has several built-in types. Here are the ones you will use most often:
Integers
package main
import "fmt"
func main() {
var a int = 42 // Platform-dependent (32 or 64 bit)
var b int8 = 127 // -128 to 127
var c int16 = 32767 // -32768 to 32767
var d int32 = 2147483647
var e int64 = 9223372036854775807
fmt.Println(a, b, c, d, e)
// Unsigned integers (no negative numbers)
var f uint = 42
var g uint8 = 255 // 0 to 255 (same as byte)
var h uint16 = 65535
var i uint32 = 4294967295
var j uint64 = 18446744073709551615
fmt.Println(f, g, h, i, j)
}
For most cases, just use int. Go will make it 64-bit on modern systems. Use specific sizes only when you need them.
Floats
package main
import "fmt"
func main() {
var price float64 = 19.99 // 64-bit float (most common)
var score float32 = 95.5 // 32-bit float (less precision)
fmt.Printf("Price: %.2f\n", price)
fmt.Printf("Score: %.1f\n", score)
}
Output:
Price: 19.99
Score: 95.5
Use float64 by default. It has more precision and is the default type for floating-point numbers in Go.
Strings
package main
import "fmt"
func main() {
// Regular string (double quotes)
greeting := "Hello, World!"
// Raw string (backticks) — no escape sequences
path := `C:\Users\Alex\Documents`
multiline := `This is
a multi-line
string`
fmt.Println(greeting)
fmt.Println(path)
fmt.Println(multiline)
// String length
fmt.Println("Length:", len(greeting))
// Access individual bytes
fmt.Printf("First byte: %c\n", greeting[0])
}
Output:
Hello, World!
C:\Users\Alex\Documents
This is
a multi-line
string
Length: 13
First byte: H
Strings in Go are immutable. You cannot change a character in a string. You need to create a new string instead.
Booleans
package main
import "fmt"
func main() {
isReady := true
isComplete := false
fmt.Println("Ready:", isReady)
fmt.Println("Complete:", isComplete)
fmt.Println("Both:", isReady && isComplete) // AND
fmt.Println("Either:", isReady || isComplete) // OR
fmt.Println("Not ready:", !isReady) // NOT
}
Output:
Ready: true
Complete: false
Both: false
Either: true
Not ready: false
Byte and Rune
package main
import "fmt"
func main() {
// byte is an alias for uint8 — represents ASCII characters
var b byte = 'A'
fmt.Printf("Byte: %c (value: %d)\n", b, b)
// rune is an alias for int32 — represents Unicode characters
var r rune = '日'
fmt.Printf("Rune: %c (value: %d)\n", r, r)
// Iterating over a string gives you runes, not bytes
word := "Hello"
for i, ch := range word {
fmt.Printf("Index %d: %c\n", i, ch)
}
}
Output:
Byte: A (value: 65)
Rune: 日 (value: 26085)
Index 0: H
Index 1: e
Index 2: l
Index 3: l
Index 4: o
Use byte for ASCII text. Use rune when working with Unicode characters.
Zero Values
This is an important concept in Go. Every type has a default value called the zero value. When you declare a variable without assigning it, it gets the zero value automatically.
package main
import "fmt"
func main() {
var i int // 0
var f float64 // 0.0
var b bool // false
var s string // "" (empty string)
fmt.Printf("int: %d\n", i)
fmt.Printf("float64: %f\n", f)
fmt.Printf("bool: %t\n", b)
fmt.Printf("string: %q\n", s) // %q shows quotes around strings
}
Output:
int: 0
float64: 0.000000
bool: false
string: ""
Here is the full list of zero values:
| Type | Zero Value |
|---|---|
int, int8, int16, int32, int64 | 0 |
uint, uint8, uint16, uint32, uint64 | 0 |
float32, float64 | 0.0 |
bool | false |
string | "" (empty string) |
| Pointers | nil |
| Slices, maps, channels | nil |
Zero values are useful. You don’t need to initialize variables to a default value. Go does it for you. This is different from some languages where uninitialized variables contain garbage data.
Constants
Constants are values that never change. Use the const keyword:
package main
import "fmt"
func main() {
const pi = 3.14159
const greeting = "Hello"
const maxRetries = 3
fmt.Println(pi)
fmt.Println(greeting)
fmt.Println(maxRetries)
// This would be an error:
// pi = 3.14 // Error: cannot assign to pi
}
You can declare multiple constants in a block:
const (
appName = "MyApp"
appVersion = "1.0.0"
maxUsers = 1000
)
Constants must be known at compile time. You cannot assign a function result to a constant:
const name = "Alex" // OK — string literal
const age = 25 // OK — number literal
// const now = time.Now() // Error — function call is not a constant
Iota — Auto-Incrementing Constants
iota is a special constant generator. It starts at 0 and increments by 1 for each constant in a block:
package main
import "fmt"
const (
Sunday = iota // 0
Monday // 1
Tuesday // 2
Wednesday // 3
Thursday // 4
Friday // 5
Saturday // 6
)
func main() {
fmt.Println("Sunday:", Sunday)
fmt.Println("Monday:", Monday)
fmt.Println("Saturday:", Saturday)
}
Output:
Sunday: 0
Monday: 1
Saturday: 6
You can use expressions with iota:
package main
import "fmt"
// File sizes using iota with bit shifting
const (
_ = iota // Skip 0
KB = 1 << (10 * iota) // 1 << 10 = 1024
MB // 1 << 20 = 1048576
GB // 1 << 30 = 1073741824
TB // 1 << 40
)
func main() {
fmt.Printf("KB: %d\n", KB)
fmt.Printf("MB: %d\n", MB)
fmt.Printf("GB: %d\n", GB)
}
Output:
KB: 1024
MB: 1048576
GB: 1073741824
iota is commonly used for enumerations and bit flags. It replaces the need for an enum keyword.
Type Conversions
Go does not convert types automatically. You must convert them explicitly:
package main
import "fmt"
func main() {
// int to float64
age := 25
ageFloat := float64(age)
fmt.Printf("Age as float: %.1f\n", ageFloat)
// float64 to int (truncates, does not round)
price := 19.99
priceInt := int(price)
fmt.Printf("Price as int: %d\n", priceInt) // 19, not 20
// int to string — does NOT do what you think!
number := 65
letter := string(number) // Converts to Unicode character, not "65"
fmt.Println(letter) // Prints: A (Unicode 65)
// To convert a number to a string, use fmt.Sprintf
numberStr := fmt.Sprintf("%d", number)
fmt.Println(numberStr) // Prints: 65
// string to int — use strconv package
// (we'll show this below)
}
Output:
Age as float: 25.0
Price as int: 19
A
65
Converting Strings to Numbers
Use the strconv package to convert between strings and numbers:
package main
import (
"fmt"
"strconv"
)
func main() {
// String to int
numStr := "42"
num, err := strconv.Atoi(numStr) // Atoi = ASCII to Integer
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Println("Number:", num)
// Int to string
str := strconv.Itoa(100) // Itoa = Integer to ASCII
fmt.Println("String:", str)
// String to float
floatStr := "3.14"
f, err := strconv.ParseFloat(floatStr, 64)
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Println("Float:", f)
// String to bool
boolStr := "true"
b, err := strconv.ParseBool(boolStr)
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Println("Bool:", b)
}
Output:
Number: 42
String: 100
Float: 3.14
Bool: true
Notice that strconv.Atoi returns two values: the number and an error. If the string is not a valid number, the error will not be nil. This is Go’s error handling pattern. We will cover it in detail in the next tutorial.
String Formatting
Go’s fmt.Sprintf is the main way to build formatted strings:
package main
import "fmt"
func main() {
name := "Alex"
age := 25
score := 95.5
// Build a formatted string
message := fmt.Sprintf("Name: %s, Age: %d, Score: %.1f", name, age, score)
fmt.Println(message)
// Padding and alignment
fmt.Printf("%-10s %5d\n", "Alex", 100) // Left-align name, right-align number
fmt.Printf("%-10s %5d\n", "Sam", 95)
fmt.Printf("%-10s %5d\n", "Jordan", 87)
// Leading zeros
fmt.Printf("ID: %05d\n", 42) // 00042
}
Output:
Name: Alex, Age: 25, Score: 95.5
Alex 100
Sam 95
Jordan 87
ID: 00042
A Complete Example
Here is a program that uses everything from this tutorial:
package main
import (
"fmt"
"strconv"
)
// Constants for our simple grading system
const (
gradeA = iota + 1 // 1
gradeB // 2
gradeC // 3
gradeD // 4
gradeF // 5
)
const passingGrade = gradeC
func main() {
fmt.Println("=== GO-3: Variables, Types, and Constants ===")
fmt.Println()
// Variables with different types
studentName := "Alex"
studentAge := 20
gpa := 3.75
isEnrolled := true
// Print student info
fmt.Printf("Student: %s\n", studentName)
fmt.Printf("Age: %d\n", studentAge)
fmt.Printf("GPA: %.2f\n", gpa)
fmt.Printf("Enrolled: %t\n", isEnrolled)
fmt.Println()
// Zero values
var unsetInt int
var unsetString string
var unsetBool bool
fmt.Printf("Zero int: %d\n", unsetInt)
fmt.Printf("Zero string: %q\n", unsetString)
fmt.Printf("Zero bool: %t\n", unsetBool)
fmt.Println()
// Type conversions
ageStr := strconv.Itoa(studentAge)
fmt.Printf("Age as string: %s\n", ageStr)
scoreStr := "95"
score, err := strconv.Atoi(scoreStr)
if err != nil {
fmt.Println("Error converting score:", err)
} else {
fmt.Printf("Score as int: %d\n", score)
}
fmt.Println()
// Constants with iota
fmt.Println("Grade A:", gradeA)
fmt.Println("Grade B:", gradeB)
fmt.Println("Passing grade:", passingGrade)
}
Output:
=== GO-3: Variables, Types, and Constants ===
Student: Alex
Age: 20
GPA: 3.75
Enrolled: true
Zero int: 0
Zero string: ""
Zero bool: false
Age as string: 20
Score as int: 95
Grade A: 1
Grade B: 2
Passing grade: 3
Common Mistakes
1. Using := to reassign an existing variable.
name := "Alex"
name := "Sam" // Error: no new variables on left side of :=
name = "Sam" // Correct: use = to reassign
Use := only for the first declaration. Use = for reassignment.
2. Forgetting that string() converts to a Unicode character.
num := 65
s := string(num) // "A", not "65"
s = fmt.Sprintf("%d", num) // "65" — this is what you want
3. Mixing types without conversion.
a := 10 // int
b := 3.5 // float64
// c := a + b // Error: mismatched types int and float64
c := float64(a) + b // Correct
Go never converts types implicitly. You must always be explicit.
Source Code
You can find the complete source code for this tutorial on GitHub:
Related Articles
- Go Tutorial #2: Installing Go — Set up Go and write your first program
What’s Next?
In the next tutorial, Go Tutorial #4: Functions and Error Handling, you will learn:
- How to write functions in Go
- Multiple return values
- The
errortype andif err != nilpattern - Variadic functions
- The
deferkeyword
This is part 3 of the Go Tutorial series. Follow along to learn Go from scratch.