Back to Notes

Go Variables & Types

Overview

Go is statically typed — every variable has a type fixed at compile time. The compiler catches type mismatches before the program runs. Types are written after the variable name (opposite to C/Java).


Basic Types

Go's built-in types cover all common needs. Use the simplest type that fits — int for whole numbers, float64 for decimals, string for text, bool for flags.

CategoryTypes
Booleanbool
Stringstring
Integerint, int8, int16, int32, int64
Unsigned intuint, uint8, uint16, uint32, uint64, uintptr
Aliasesbyte = uint8 · rune = int32 (Unicode code point)
Floatfloat32, float64
Complexcomplex64, complex128

Rule of thumb: Use int for integers, float64 for decimals. Sized variants (int32, etc.) are only needed when interfacing with binary formats or C code.


Zero Values

Every variable declared without an explicit value gets a zero value automatically — Go has no undefined/null surprises.

TypeZero value
Numeric0
Booleanfalse
String""
Pointer / interface / map / slice / channel / functionnil
var i int     // 0
var f float64 // 0
var b bool    // false
var s string  // ""

fmt.Printf("%v %v %v %q\n", i, f, b, s)
// Output: 0 0 false ""

Variable Declaration

var — explicit declaration

Use var at package level or when you want to declare without initialising immediately.

// Package level (outside any function)
var globalCounter int
var appName string = "MyApp"

// Function level — type inferred from value
var x = 42          // int
var msg = "hello"   // string

// Multiple in one statement
var i, j int = 1, 2
var a, b, c = true, false, "no!"

:= — short declaration (most common)

The walrus operator declares and assigns in one step. Only works inside functions. The type is inferred from the right-hand side.

func main() {
    k := 5            // int
    name := "Vatsal"  // string
    pi := 3.14        // float64
    flag := true      // bool

    // Multiple assignment
    x, y := 10, 20
}

var vs := — when to use which

Use var whenUse := when
Declaring at package levelInside a function (most cases)
Declaring without initialisingYou have an initial value
Grouping related declarationsOne-off quick declaration
Zero value is meaningfulType inference is fine
// var — zero value intentional
var retries int   // starts at 0, will be updated later

// := — value known immediately
count := 0
name := "Alice"

Type Conversion

Go has no implicit type conversion — you must convert explicitly with T(value). This prevents accidental precision loss or unexpected behaviour.

var x, y int = 3, 4
f := math.Sqrt(float64(x*x + y*y))  // must cast int → float64
z := int(f)                          // and back — truncates, doesn't round

var i int = 42
var f64 float64 = float64(i)
var u uint = uint(f64)

Common mistake: math.Sqrt takes float64 — passing int directly is a compile error.


Type Inference

When a value is provided, Go infers the type — you don't need to spell it out. The inferred type follows the value's natural type.

i := 42        // int
f := 3.142     // float64
g := 0.867 + 0.5i  // complex128
s := "hello"   // string

// Verify with %T
fmt.Printf("%T\n", f)   // float64

Constants

Constants are values known at compile time. They cannot be assigned to variables that change at runtime (no time.Now(), no function calls).

const Pi = 3.14159
const Greeting = "Hello"
const MaxSize = 1024
const IsDebug = false

Grouped constants

const (
    StatusOK    = 200
    StatusNotFound = 404
    AppName     = "GoApp"
)

Computed constants

Constants can reference other constants — computed at compile time:

const firstName = "Lane"
const lastName  = "Wagner"
const fullName  = firstName + " " + lastName  // ✅ compile-time concat

// const now = time.Now()  // ❌ runtime call — not allowed

Numeric constants — untyped and high-precision

An untyped numeric constant takes whatever type the context needs. This lets a single constant be used with different numeric types.

const Big   = 1 << 62    // fits in int64
const Small = Big >> 61  // = 2

func needInt(x int) int         { return x*10 + 1 }
func needFloat(x float64) float64 { return x * 0.1 }

needInt(Small)    // ✅ constant 2 used as int
needFloat(Big)    // ✅ constant used as float64
needInt(Big)      // ❌ overflow — Big too large for int

iota — Auto-incrementing Enumerations

iota starts at 0 within a const block and increments by 1 for each constant. Perfect for enums.

type Weekday int

const (
    Sunday    Weekday = iota  // 0
    Monday                    // 1
    Tuesday                   // 2
    Wednesday                 // 3
    Thursday                  // 4
    Friday                    // 5
    Saturday                  // 6
)

// With expressions
type ByteSize float64

const (
    _           = iota  // ignore first value
    KB ByteSize = 1 << (10 * iota)  // 1 << 10 = 1024
    MB                               // 1 << 20
    GB                               // 1 << 30
)

String Formatting

Go uses fmt for formatted output. Printf prints to stdout, Sprintf returns a string.

name := "Vatsal"
age  := 25
gpa  := 8.75

fmt.Printf("Name: %s, Age: %d, GPA: %.2f\n", name, age, gpa)
// Name: Vatsal, Age: 25, GPA: 8.75

s := fmt.Sprintf("Hello, %s! You are %d years old.", name, age)
// s = "Hello, Vatsal! You are 25 years old."

Format Verbs

VerbUseExample
%vDefault format (any type)42, true, [1 2 3]
%+vStruct with field names{X:1 Y:2}
%#vGo-syntax representationmain.Vertex{X:1, Y:2}
%TType nameint, main.Vertex
%sString / byte slicehello
%qQuoted string"hello"
%dInteger (base 10)42
%bInteger (binary)101010
%xInteger (hex, lowercase)2a
%fFloat10.523000
%.2fFloat (2 decimal places)10.52
%eScientific notation1.052300e+01
%tBooleantrue
%pPointer address0xc000018030

Variable Shadowing

Inner scopes can redeclare the same name with :=, hiding the outer variable. This is a common source of bugs.

x := 10
if true {
    x := 20        // NEW x, shadows outer x
    fmt.Println(x) // 20
}
fmt.Println(x)     // 10 — outer x unchanged

// Fix: use = instead of := to assign to the outer variable
x = 20

Gotchas

GotchaDetail
:= outside functionNot allowed — use var at package level
Unused variablesCompile error — Go forces you to use what you declare
No implicit conversionfloat64(x) not just x — explicit always
Shadowing with :=Easy to accidentally create a new variable instead of updating the outer one
const cannot be runtime valueconst t = time.Now() — compile error