Table of Contents
- Go Syntax Basics
- Contents
- Variables
- Constants & iota
- Basic types
- Zero values
- Type conversion
- Control flow
- Functions
- Multiple & named returns
- defer
Go Syntax Basics¶
Contents¶
- Variables
- Constants & iota
- Basic types
- Zero values
- Type conversion
- Control flow
- Functions
- Multiple & named returns
- defer
Variables¶
var a int // declared, zero value 0
var b = 10 // type inferred (int)
var c, d = 1, "two" // multiple, mixed types
var ( // grouped
e bool
f float64 = 3.14
)
g := 42 // short decl, only inside functions
h, err := doThing() // common idiom; at least one new var on LHS
_ = g // blank identifier discards a value
Rules: every declared local variable must be used (compile error otherwise). := is function-body only; package-level needs var.
Constants & iota¶
Constants are compile-time, untyped unless given a type. They can be numbers, strings, booleans, runes.
const Pi = 3.14159
const Greeting string = "hi"
// Untyped constants adapt to context:
const big = 1 << 62 // fine as a constant
var x float64 = big // converts on use
// iota: resets to 0 per const block, increments per line.
type Weekday int
const (
Sunday Weekday = iota // 0
Monday // 1
Tuesday // 2
)
// Bit flags
const (
FlagA = 1 << iota // 1
FlagB // 2
FlagC // 4
)
// Skip values with _ and use expressions
const (
_ = iota // ignore 0
KB = 1 << (10 * iota) // 1<<10
MB // 1<<20
GB // 1<<30
)
Basic types¶
bool
string
int int8 int16 int32 int64 // int is 32 or 64 bit (platform)
uint uint8 uint16 uint32 uint64 uintptr
byte // alias for uint8
rune // alias for int32, a Unicode code point
float32 float64
complex64 complex128
int/uint size is platform-dependent; use sized types when it matters (serialization, bit ops).
Zero values¶
No uninitialized memory in Go. Every type has a zero value:
| Type | Zero |
|---|---|
| numeric | 0 |
| bool | false |
| string | "" |
| pointer, slice, map, chan, func, interface | nil |
| struct | each field zeroed |
| array | each element zeroed |
var s []int // nil slice: len 0, cap 0, safe to range/append
var m map[string]int // nil map: reads ok (zero value), writes PANIC
var p *int // nil pointer
Type conversion¶
Go has no implicit numeric conversion. Convert explicitly:
Control flow¶
// if with optional init statement (scope limited to if/else)
if v, ok := m["k"]; ok {
use(v)
} else {
// v still in scope here
}
// for is the only loop keyword
for i := 0; i < 10; i++ {} // classic
for cond {} // while-style
for {} // infinite
for i := range 5 {} // 0..4 (Go 1.22+)
for i, v := range slice {} // index, value
for k, v := range mp {} // key, value (random order)
for r := range "héllo" {} // r is a rune; index jumps by UTF-8 width
// switch: no fallthrough by default; cases can be expressions
switch x := f(); {
case x < 0:
neg()
case x == 0:
zero()
default:
pos()
}
switch x {
case 1, 2, 3: // multiple values
small()
case 4:
fallthrough // explicit fallthrough to next case
case 5:
medium()
}
// label + break/continue for nested loops
outer:
for _, row := range grid {
for _, c := range row {
if c == 'x' {
break outer
}
}
}
goto done // exists but rarely idiomatic
done:
Functions¶
func add(a, b int) int { return a + b } // shared type for a,b
func variadic(prefix string, nums ...int) int {
sum := 0
for _, n := range nums { sum += n }
return sum
}
variadic("x", 1, 2, 3)
xs := []int{1, 2, 3}
variadic("x", xs...) // spread a slice
// Functions are values / first-class
var op func(int, int) int = add
apply := func(f func(int, int) int, a, b int) int { return f(a, b) }
// Closures capture variables by reference
func counter() func() int {
n := 0
return func() int { n++; return n }
}
Multiple & named returns¶
func divmod(a, b int) (int, int) {
return a / b, a % b
}
q, r := divmod(17, 5)
// Named return values: pre-declared, zero-valued, settable by defer
func read(path string) (data []byte, err error) {
defer func() {
if err != nil {
err = fmt.Errorf("read %s: %w", path, err)
}
}()
data, err = os.ReadFile(path)
return // "naked" return uses named values
}
Idiom: return (value, error) with error last; check if err != nil immediately.
defer¶
Deferred calls run LIFO when the surrounding function returns (normally or via panic).
f, err := os.Open(name)
if err != nil { return err }
defer f.Close() // runs at function exit
// Arguments are evaluated WHEN defer is reached, not when it runs:
i := 0
defer fmt.Println(i) // prints 0
i++
// But closures see the latest value:
defer func() { fmt.Println(i) }() // prints 1
// LIFO ordering
for i := 0; i < 3; i++ {
defer fmt.Print(i) // prints 210
}
Common uses: closing files/connections, unlocking mutexes, recovering from panics, modifying named return values.