Emergency server help: get in touch

Go Cheat Sheet: Syntax, Slices, Errors and Goroutines

A compact Go 1.27 reference covering the go command, types, slices, maps, interfaces, generics, errors, concurrency and file handling.

Published 7 min read

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.

The go command

CommandWhat it does
go mod init example.com/appCreate 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@latestAdd or upgrade a dependency
go mod tidyAdd 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@latestInstall a command into $(go env GOPATH)/bin
go doc strings.FieldsDocumentation in the terminal

Variables, types and control flow

SyntaxWhat it doesExample
x := 42Declare and infer the type (inside functions only)name, port := "web01", 443
var y float64 = 3.5Explicit declaration; unset variables get the zero valuevar count int // 0
const + iotaEnumerated constantsconst ( Low Level = iota; Medium; High )
for i := 0; i < n; i++Classic loop; for is Go’s only loop keyword
for i := range 10Loop over an integer (Go 1.22+)
for i, v := range sIndex and value of a slice, string or mapfor k, v := range m
for cond { } / for { }While loop / infinite loop
if v, err := f(); err != nilif with a short statement
switch x { case 1, 2: ... }No fall-through by defaultswitch { 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

SyntaxWhat 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.TrimSpaceCommon tests and trimming
var sb strings.BuilderEfficient 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 verbPrints
%v / %+vValue / value with field names
%TType name
%qQuoted string
%d, %5.2f, %xInteger, 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

SyntaxWhat it does
return 0, errErrors 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

SyntaxWhat 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 := <-chSend / receive
close(ch) + for v := range chSender 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)
    }
}
SyntaxWhat 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.goTest 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

Go Cheat Sheet – short answer
In short: A compact Go 1.27 reference covering the go command, types, slices, maps, interfaces, generics, errors, concurrency and file handling.
Go Cheat Sheet – what this guide covers
Go Cheat Sheet: what this guide covers.

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.

Share this guide

Free website test

Is your website set up right?

Check SSL, security headers, redirects, robots.txt, sitemap, llms.txt and security.txt in one test. It takes about 30 seconds.