Short answer: Start a project with go mod init example.com/app, run it with go run . and build a static binary with CGO_ENABLED=0 go build. Declare with :=, loop with for (including for i := range 10), and return errors as the last value, wrapping them with fmt.Errorf("...: %w", err). Use slices and maps with the slices and maps packages, goroutines with sync.WaitGroup, and context for timeouts. Go 1.27 adds generic methods.
The examples in this Go cheat sheet were run with Go 1.27.2 on Linux on 9 October 2026, and checked with go vet and gofmt.
Table of Contents
The go command
| Command | What it does |
|---|---|
go mod init example.com/app | Create go.mod for a new module |
go run . | Compile and run the package in the current directory |
go build -o app . | Build a binary |
CGO_ENABLED=0 go build -ldflags="-s -w" -o app . | Static binary without debug symbols; copies cleanly to any Linux server |
GOOS=linux GOARCH=arm64 go build -o app-arm64 . | Cross-compile, e.g. for an ARM server |
go get example.com/pkg@latest | Add or upgrade a dependency |
go mod tidy | Add missing and remove unused modules |
go test ./... | Run all tests; add -race for the race detector, -cover for coverage |
go vet ./... | Report suspicious code |
gofmt -l -w . | Format code in place (go fmt ./... does the same) |
go fix ./... | Apply the modernisers that update code to newer idioms (rewritten in Go 1.26) |
go install example.com/tool@latest | Install a command into $(go env GOPATH)/bin |
go doc strings.Fields | Documentation in the terminal |
Variables, types and control flow
| Syntax | What it does | Example |
|---|---|---|
x := 42 | Declare and infer the type (inside functions only) | name, port := "web01", 443 |
var y float64 = 3.5 | Explicit declaration; unset variables get the zero value | var count int // 0 |
const + iota | Enumerated constants | const ( Low Level = iota; Medium; High ) |
for i := 0; i < n; i++ | Classic loop; for is Go’s only loop keyword | |
for i := range 10 | Loop over an integer (Go 1.22+) | |
for i, v := range s | Index and value of a slice, string or map | for k, v := range m |
for cond { } / for { } | While loop / infinite loop | |
if v, err := f(); err != nil | if with a short statement | |
switch x { case 1, 2: ... } | No fall-through by default | switch { case n > 10: ... } |
defer f.Close() | Run when the function returns (LIFO order) | |
strconv.Atoi("8080") | String to int, returns (int, error) | strconv.Itoa(80) |
Slices, maps and strings
| Syntax | What it does |
|---|---|
s := []int{5, 3, 8} | Slice literal |
make([]int, 0, 100) | Empty slice with capacity 100 |
s = append(s, 1, 9) | Append; always assign the result back |
s[1:3] | Sub-slice sharing the same backing array |
slices.Sort(s), slices.Contains(s, 8), slices.Index(s, 8) | Helpers from the slices package |
m := map[string]int{"ssh": 22} | Map literal; make(map[string]int) for an empty one |
v, ok := m["ssh"] | Lookup with presence check (missing keys give the zero value) |
delete(m, "ssh") | Remove a key |
slices.Sorted(maps.Keys(m)) | Sorted keys; map iteration order is random |
strings.Fields(line) | Split on any whitespace |
strings.Split(s, ","), strings.Join(parts, ",") | Split and join |
strings.HasPrefix, strings.Contains, strings.TrimSpace | Common tests and trimming |
var sb strings.Builder | Efficient string building |
Writing to a nil map panics: create it with make or a literal first. A sub-slice shares memory with the original, so append on one can overwrite the other; use slices.Clone when you need a copy.
Structs, methods and interfaces
type Server struct {
Name string `json:"name"`
Port int `json:"port"`
TLS bool `json:"tls,omitempty"`
}
func (s Server) String() string { return fmt.Sprintf("%s:%d", s.Name, s.Port) } // value receiver
func (s *Server) SetPort(p int) { s.Port = p } // pointer receiver modifies
type Shape interface{ Area() float64 }
type Square struct{ Side float64 }
func (q Square) Area() float64 { return q.Side * q.Side } // Square satisfies Shape implicitly
var sh Shape = Square{Side: 3}
switch v := sh.(type) { // type switch
case Square:
fmt.Println("square", v.Side)
}
| Format verb | Prints |
|---|---|
%v / %+v | Value / value with field names |
%T | Type name |
%q | Quoted string |
%d, %5.2f, %x | Integer, float with width and precision, hex |
Generics
func Max[T cmp.Ordered](a, b T) T {
if a > b {
return a
}
return b
}
type Stack[T any] struct{ items []T }
func (s *Stack[T]) Push(v T) { s.items = append(s.items, v) }
// Go 1.27: a method can declare its own type parameters
func (s *Stack[T]) Map[U any](f func(T) U) []U {
out := make([]U, 0, len(s.items))
for _, v := range s.items {
out = append(out, f(v))
}
return out
}
Max(3, 7) // 7
Max("a", "b") // "b"
Constraints come from cmp (cmp.Ordered) or your own interfaces such as interface{ ~int | ~int64 }. Interface methods still cannot have type parameters. Go 1.26 also lets new take an expression, so new(5) returns an *int pointing at 5, which is handy for optional JSON fields.
Errors, defer and panic
| Syntax | What it does |
|---|---|
return 0, err | Errors are ordinary values, returned last |
errors.New("not found") | Simple error; declare sentinels at package level |
fmt.Errorf("lookup %q: %w", name, err) | Add context and wrap the original |
errors.Is(err, ErrNotFound) | Match a sentinel anywhere in the chain |
errors.As(err, &pathErr) | Extract a specific error type, e.g. *fs.PathError |
panic(...) / recover() | For programmer bugs only; recover works inside a deferred function |
var ErrNotFound = errors.New("not found")
func lookup(name string) (int, error) {
if name == "ssh" {
return 22, nil
}
return 0, fmt.Errorf("lookup %q: %w", name, ErrNotFound)
}
_, err := lookup("ftp")
fmt.Println(err) // lookup "ftp": not found
fmt.Println(errors.Is(err, ErrNotFound)) // true
Goroutines, channels and context
| Syntax | What it does |
|---|---|
go f() | Start a goroutine |
var wg sync.WaitGroup; wg.Go(fn); wg.Wait() | Run tasks and wait for all (WaitGroup.Go since Go 1.25) |
ch := make(chan int, 3) | Buffered channel; omit the size for unbuffered |
ch <- v / v := <-ch | Send / receive |
close(ch) + for v := range ch | Sender closes; receiver loops until closed |
select { case v := <-a: ... case <-ctx.Done(): ... } | Wait on several channels |
var mu sync.Mutex; mu.Lock(); defer mu.Unlock() | Protect shared data |
ctx, cancel := context.WithTimeout(ctx, 5*time.Second) | Deadline for a request; always defer cancel() |
var (
wg sync.WaitGroup
mu sync.Mutex
total int
)
for i := range 5 {
wg.Go(func() { // each iteration has its own i since Go 1.22
mu.Lock()
defer mu.Unlock()
total += i
})
}
wg.Wait() // total == 10
Files, JSON, tests and a log example
For server admins: count HTTP status codes in a combined-format access log, the Go version of awk '{print $9}' | sort | uniq -c.
package main
import (
"bufio"
"fmt"
"log"
"os"
"strings"
)
func main() {
f, err := os.Open(os.Args[1])
if err != nil {
log.Fatal(err)
}
defer f.Close()
counts := map[string]int{}
sc := bufio.NewScanner(f)
for sc.Scan() {
fields := strings.Fields(sc.Text())
if len(fields) > 8 {
counts[fields[8]]++
}
}
if err := sc.Err(); err != nil {
log.Fatal(err)
}
for code, n := range counts {
fmt.Printf("%7d %s\n", n, code)
}
}
| Syntax | What it does |
|---|---|
os.ReadFile(path) / os.WriteFile(path, data, 0o644) | Whole file in or out |
bufio.NewScanner(f) | Line by line; lines over 64 KB need sc.Buffer(...) |
json.Marshal(v) / json.Unmarshal(data, &v) | Encode / decode using struct tags |
exec.Command("systemctl", "is-active", "nginx").Output() | Run a command without a shell |
http.HandleFunc("/health", h); http.ListenAndServe(":8080", nil) | Minimal HTTP server |
func TestX(t *testing.T) in x_test.go | Test function, run with go test |
func TestLookup(t *testing.T) {
tests := []struct {
name string
want int
wantErr bool
}{
{"ssh", 22, false},
{"ftp", 0, true},
}
for _, tt := range tests {
got, err := lookup(tt.name)
if (err != nil) != tt.wantErr || got != tt.want {
t.Errorf("lookup(%q) = %d, %v", tt.name, got, err)
}
}
}
Official documentation (checked 9 October 2026): The Go Programming Language Specification · Effective Go · Go 1.27 Release Notes · Go 1.26 Release Notes · Command go · Go standard library
Go Cheat Sheet at a glance


Related: Rust Cheat Sheet: Cargo, Ownership, Enums and Errors · C Cheat Sheet: C17 and C23 Syntax, Memory and gcc Flags · JSON Formatter and Validator.
Reading a lot of our guides? Go ad-free with srvScripts Pro for $6 a month. It also pays for new guides. Cancel any time.