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() {
    // explicit type and value
    var name string = "Manikandan"

    // type inferred from the right-hand side
    var age = 30

    // shorthand declaration (only inside functions)
    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:

  1. var name string = "Manikandan" — the most explicit form. You write var, the variable name, the type, then the value. Verbose but unambiguous.
  2. var age = 30 — same as above but Go figures out the type from the value. 30 is a whole number, so Go infers int.
  3. 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, var for package-level variables (which we’ll see later).

Declaring multiple variables at once

Go lets you declare several variables on a single line. This is handy when you want to group related values together:

// inside main()
a, b, c := 10, 20, 30

fmt.Println(a, b, c)
10 20 30

The types don’t need to match — Go infers each one independently:

// inside main()
name, age, active := "Manikandan", 30, true

fmt.Println(name, age, active)

You can also do this with var, and even group declarations in a block:

// inside main()
var x, y int = 1, 2

var (
    host    = "localhost"
    port    = 8080
    enabled = true
)

fmt.Println(x, y, host, port, enabled)

The var (...) block is the standard way to declare several package-level variables together — you’ll see it in real Go projects all the time.

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
  • int zero value is 0
  • string zero value is "" (an empty string — that’s why name printed as nothing)
  • bool zero value is false

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. Here’s the full list:

CategoryTypeDescriptionZero value
Booleanbooltrue or falsefalse
StringstringImmutable sequence of bytes (UTF-8 text)""
Signed integerintPlatform-sized integer (32- or 64-bit)0
int8, int16, int32, int64Fixed-size signed integers0
Unsigned integeruintPlatform-sized unsigned integer0
uint8, uint16, uint32, uint64Fixed-size unsigned integers0
uintptrUnsigned integer big enough to hold a pointer0
Floating pointfloat32, float64IEEE-754 floating-point numbers0
Complexcomplex64, complex128Complex numbers (real + imaginary parts)0+0i
AliasesbyteAlias for uint8 — used for raw binary data0
runeAlias for int32 — represents a Unicode code point0

In day-to-day code you’ll mostly reach for int, float64, string, and bool. The sized variants exist for when you need to control memory layout exactly (file formats, network protocols, embedded systems).

A quick taste of each:

// inside main()
var count int = 42
var pi float64 = 3.14159
var greeting string = "Hello"
var ready bool = true

fmt.Println(count, pi, greeting, ready)

Strings deserve one extra note. They’re wrapped in double quotes "..." and are immutable — once created, a string can never be changed. For multi-line strings or text 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)

Go has three different quote marks — double quotes, single quotes, and backticks — and each one means something different. The String Quotes Cheatsheet explains when to use which.

Numeric literals can use underscores for readability: 1_000_000 is the same as 1000000. Go ignores them.

Printing values

You’ve been using fmt.Println to print everything so far. When you need more control — a specific number of decimals, hex output, struct field names — switch to fmt.Printf and use a format verb for each value:

// inside main()
count := 42
pi := 3.14159
greeting := "Hello"
ready := true

fmt.Printf("%d %f %s %t\n", count, pi, greeting, ready)
fmt.Printf("%v %T\n", count, count) // value, then type
42 3.141590 Hello true
42 int

The five verbs you’ll reach for most:

VerbUse it for
%vAny value, default format — when in doubt, use this
%dInteger (base 10)
%sString
%fFloating-point number
%TThe type of the value (great for debugging)

There’s a whole family of verbs for hex, binary, width, precision, and struct formatting — the fmt Printing Cheatsheet at the end of the course covers them in full.

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:

  • camelCase for variables and functions used inside one file/package: userName, currentAge
  • PascalCase for 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.

Toggle theme (T)