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.
| Category | Types |
|---|---|
| Boolean | bool |
| String | string |
| Integer | int, int8, int16, int32, int64 |
| Unsigned int | uint, uint8, uint16, uint32, uint64, uintptr |
| Aliases | byte = uint8 · rune = int32 (Unicode code point) |
| Float | float32, float64 |
| Complex | complex64, complex128 |
Rule of thumb: Use
intfor integers,float64for 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.
| Type | Zero value |
|---|---|
| Numeric | 0 |
| Boolean | false |
| String | "" |
| Pointer / interface / map / slice / channel / function | nil |
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 when | Use := when |
|---|---|
| Declaring at package level | Inside a function (most cases) |
| Declaring without initialising | You have an initial value |
| Grouping related declarations | One-off quick declaration |
| Zero value is meaningful | Type 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.Sqrttakesfloat64— passingintdirectly 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
| Verb | Use | Example |
|---|---|---|
%v | Default format (any type) | 42, true, [1 2 3] |
%+v | Struct with field names | {X:1 Y:2} |
%#v | Go-syntax representation | main.Vertex{X:1, Y:2} |
%T | Type name | int, main.Vertex |
%s | String / byte slice | hello |
%q | Quoted string | "hello" |
%d | Integer (base 10) | 42 |
%b | Integer (binary) | 101010 |
%x | Integer (hex, lowercase) | 2a |
%f | Float | 10.523000 |
%.2f | Float (2 decimal places) | 10.52 |
%e | Scientific notation | 1.052300e+01 |
%t | Boolean | true |
%p | Pointer address | 0xc000018030 |
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
| Gotcha | Detail |
|---|---|
:= outside function | Not allowed — use var at package level |
| Unused variables | Compile error — Go forces you to use what you declare |
| No implicit conversion | float64(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 value | const t = time.Now() — compile error |