A variable is a named container that holds a value. Every variable in Go has a type — a label that tells Go what kind of value it can hold (a number, a piece of text, a true/false flag, etc.).
Go is a statically typed language. That means once a variable’s type is decided, it can never hold a value of a different type. This sounds restrictive, but it’s the language’s superpower: most of the bugs in dynamic languages get caught at compile time in Go, before your program ever runs.
Declaring variables
There are three ways to create a variable in Go. Here they all are in one runnable file:
package main
import "fmt"
func main() {
var name string = "Manikandan"
var age = 30
city := "Chennai"
fmt.Println(name, age, city)
}
Run it:
go run main.go
Manikandan 30 Chennai
Each style does the same thing — creates a variable and gives it a value. The differences:
var name string = "Manikandan"— the most explicit form. You writevar, the variable name, the type, then the value. Verbose but unambiguous.var age = 30— same as above but Go figures out the type from the value.30is a whole number, so Go infersint.city := "Chennai"— the short declaration. Only valid inside functions. This is the form you’ll write 90% of the time.
All three produce identical results. Use
:=inside functions,varfor package-level variables (which we’ll see later).
Zero values
If you declare a variable without assigning it a value, Go fills it with a sensible default called the zero value:
// inside main()
var count int
var name string
var ready bool
fmt.Println(count, name, ready)
0 false
intzero value is0stringzero value is""(an empty string — that’s whynameprinted as nothing)boolzero value isfalse
This is a deliberate Go design choice. There are no null, nil, or undefined surprises for basic types — every variable always has a real value.
The basic types
Go has a small, focused set of built-in types. These are the ones you’ll use most:
Numbers
// inside main()
var i int = 42 // whole number, sized for your CPU (usually 64-bit)
var pi float64 = 3.14 // decimal number, 64-bit
var big int64 = 9_000_000_000 // explicit 64-bit integer
var small int8 = -128 // 8-bit integer (range -128 to 127)
fmt.Println(i, pi, big, small)
For most cases, just use int for whole numbers and float64 for decimals. The sized variants (int8, int16, int32, int64, uint8, etc.) are there when you need to control memory exactly.
The underscores in
9_000_000_000are purely visual — Go ignores them. They make big numbers easier to read.
Strings
// inside main()
greeting := "Hello"
name := "Manikandan"
fmt.Println(greeting + ", " + name + "!")
Hello, Manikandan!
Strings in Go are wrapped in double quotes "...". They are immutable — once created, a string can never be changed. (You can create a new string from an old one, but the original is untouched.)
If you want a multi-line string or one with special characters, use backticks:
// inside main()
poem := `Roses are red
Violets are blue
Go is a language
And so are you`
fmt.Println(poem)
Booleans
// inside main()
isReady := true
isDone := false
fmt.Println(isReady, isDone)
A bool is either true or false. That’s all.
Type conversions
Go does not convert between types automatically. If you try to add an int to a float64, the compiler rejects it. You have to convert explicitly:
// inside main()
i := 10
f := 3.5
// total := i + f // ❌ compile error
total := float64(i) + f // ✅ explicit conversion
fmt.Println(total)
13.5
This feels annoying at first but saves you from a category of subtle bugs that plague languages like JavaScript and Python. When you write float64(i), you’re telling both the compiler and the next person who reads your code: “yes, I meant to do this.”
Constants
A constant is a value that can never change after it’s declared. Use the keyword const:
package main
import "fmt"
const Pi = 3.14159
const AppName = "GoTutor"
const MaxRetries = 3
func main() {
fmt.Println(AppName, "version", MaxRetries)
fmt.Println("Pi is approximately", Pi)
}
GoTutor version 3
Pi is approximately 3.14159
Constants are perfect for values that should never change during the program’s life — configuration values, mathematical constants, or fixed identifiers.
You can group related constants in a const block:
const (
StatusActive = "active"
StatusInactive = "inactive"
StatusBanned = "banned"
)
The compiler will refuse to compile code that tries to reassign a constant — a guarantee no comment or convention can ever match.
Type definitions
Sometimes the built-in types aren’t descriptive enough. Go lets you create your own type that’s based on an existing one:
package main
import "fmt"
type UserID int
type Email string
func main() {
var id UserID = 42
var addr Email = "[email protected]"
fmt.Println(id, addr)
}
UserID is a brand-new type whose underlying type is int. They behave the same — you can do math on a UserID — but Go treats them as different types in your code:
// inside main()
var id UserID = 42
var n int = 10
// total := id + n // ❌ compile error
total := int(id) + n // ✅ explicit conversion
fmt.Println(total)
This is hugely useful in real code. If a function expects a UserID, you can never accidentally pass it a random int. The compiler catches the mistake immediately.
Naming conventions
Go has a strict, simple naming convention:
camelCasefor variables and functions used inside one file/package:userName,currentAgePascalCasefor variables and functions visible to other packages:UserName,CurrentAge
The case of the first letter isn’t just style — it’s how Go decides what’s exported (visible outside the package) and what’s not. We’ll come back to this in the Packages section.
What’s next
You now know how to store data in Go. In the next lesson, we’ll do something with that data — comparing values, doing math, and making decisions with operators and conditionals.