Syntax
Go syntax in one sitting: declarations, types, control flow, functions, structs, slices, maps, errors. The compiler is your teammate.
Go — syntax tour
EXAMPLE
// ===== Packages =====
package main
import (
"errors"
"fmt"
"strings"
)
// ===== Variables and constants =====
var name string = "Alex"
var age = 30 // type inferred
title := "engineer" // short declaration, function scope only
const Pi = 3.14159
const (
Red = "R"
Green = "G"
)
// ===== Basic types =====
// bool, string
// int, int8/16/32/64, uint8 (byte), uint16/32/64
// float32, float64
// complex64, complex128
// rune (== int32, a Unicode code point)
// Zero values: 0, '', false, nil
// ===== Control flow =====
func classify(n int) string {
if n < 0 {
return "neg"
} else if n == 0 {
return "zero"
}
switch {
case n < 10: return "small"
case n < 100: return "medium"
default: return "large"
}
}
// Loops: there is only 'for'
func sum(xs []int) int {
total := 0
for i, x := range xs {
_ = i
total += x
}
return total
}
// ===== Functions =====
func divide(a, b float64) (float64, error) {
if b == 0 {
return 0, errors.New("division by zero")
}
return a / b, nil
}
// Variadic + multiple returns
func minMax(xs ...int) (min, max int) {
if len(xs) == 0 { return 0, 0 }
min, max = xs[0], xs[0]
for _, x := range xs[1:] {
if x < min { min = x }
if x > max { max = x }
}
return
}
// ===== Structs and methods =====
type User struct {
ID int
Name string
Email string
}
func (u User) Display() string {
return fmt.Sprintf("#%d %s <%s>", u.ID, u.Name, u.Email)
}
func (u *User) Rename(n string) { // pointer receiver: can mutate
u.Name = n
}
// ===== Slices and maps =====
nums := []int{1, 2, 3, 4}
nums = append(nums, 5)
slice := nums[1:3] // [2, 3]
m := map[string]int{"a": 1, "b": 2}
m["c"] = 3
v, ok := m["a"] // ok = present?
delete(m, "b")
for k, v := range m {
fmt.Println(k, v)
}
// ===== Interfaces =====
type Stringer interface { String() string }
type Money struct { Cents int64; Currency string }
func (m Money) String() string { return fmt.Sprintf("%.2f %s", float64(m.Cents)/100, m.Currency) }
// Money satisfies Stringer implicitly (no 'implements' keyword).
// ===== Errors are values =====
func main() {
res, err := divide(10, 0)
if err != nil {
fmt.Println("error:", err)
return
}
fmt.Println("result:", res)
fmt.Println(strings.ToUpper(name), age, title, classify(42))
fmt.Println(minMax(3, 1, 4, 1, 5, 9, 2, 6))
}
// ===== Patterns to internalise =====
// - := only inside functions; var at package scope
// - Tabs to indent (gofmt enforces). Never spaces in Go source.
// - Errors are returned, not thrown. if err != nil { return err } is the loop.
// - Slices share backing arrays; copy() when you need independent storage.
// - Maps have no ordered iteration; for stable order, sort the keys.
// - Pointer receiver when the method mutates the struct or the struct is large.
// ===== Pitfalls =====
// - Capturing loop variables in goroutines: var x := x before launching the goroutine
// - Returning a slice into a local array -> dangling? No, slices keep the array alive (could leak)
// - nil map: reading is ok, writing panics. Make with make(map[K]V).
// - Unused imports / variables -> compile error; tidy with goimports.
// - 'else' on next line after closing brace -> syntax error; Go forces braces and same-line else.
Why it matters
Go is small on purpose. Once you internalise := vs var, slices/maps/structs/interfaces, and the if err != nil reflex, you can read any Go codebase. The compiler argues with you up front so production stops arguing later.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Example
Example
package main
import "fmt"
func main() {
name := "Ada" // short var declaration
fmt.Printf("Hello, %s!\n", name)
}
Try it Yourself »
Discussion
Loading…