Skip to content

Instantly share code, notes, and snippets.

@jonlabelle
Last active February 1, 2026 15:20
Show Gist options
  • Select an option

  • Save jonlabelle/0f24799606ec5cc2e6199f1a6cbbaf75 to your computer and use it in GitHub Desktop.

Select an option

Save jonlabelle/0f24799606ec5cc2e6199f1a6cbbaf75 to your computer and use it in GitHub Desktop.
Go language cheatsheet, written to be something you can actually keep open while coding.

Go (Golang) Language Cheatsheet

Comprehensive Go language cheatsheet, written to be something you can actually keep open while coding.

It includes core syntax, language rules, standard patterns, and essential “Go-isms”.


Table of Contents

  1. Program Structure
  2. Variables & Constants
  3. Primitive Types
  4. Operators
  5. Control Flow
  6. Functions
  7. Pointers
  8. Arrays
  9. Slices
  10. Maps
  11. Structs
  12. Methods & Receivers
  13. Interfaces
  14. Generics
  15. Error Handling
  16. defer, panic, recover
  17. Concurrency
  18. Channels
  19. Packages & Modules
  20. Visibility & Naming
  21. Memory & Zero Values
  22. Common Gotchas
  23. Idiomatic Go Rules

Program Structure

package main

import "fmt"

func main() {
    fmt.Println("Hello, Go")
}
  • Every executable has package main
  • Entry point is func main()
  • Imports are explicit

Variables & Constants

Short Declaration

x := 10

Explicit Declaration

var x int = 10
var y = 20
var z int

Constants

const Pi = 3.14
const (
    A = 1
    B = 2
)
  • Constants are compile-time
  • No const slices, maps, or structs

Primitive Types

Numeric

int, int8, int16, int32, int64
uint, uint8, uint16, uint32, uint64
float32, float64
complex64, complex128

Aliases

byte // uint8
rune // int32 (Unicode)

Other

bool
string

Operators

Arithmetic

+  -  *  /  %

Comparison

==  !=  <  <=  >  >=

Logical

&&  ||  !

Bitwise

&  |  ^  <<  >>  &^

Assignment

=  +=  -=  *=  /=  %=
  • ❌ No ternary operator
  • ❌ No operator overloading

Control Flow

If

if x > 0 {
}

With initializer:

if v := get(); v > 0 {
}

For (only loop)

for i := 0; i < 10; i++ {}
for condition {}
for {}

Range:

for i, v := range slice {}
for k, v := range map {}

Switch

switch x {
case 1:
case 2:
default:
}

Type switch:

switch v := x.(type) {
case int:
case string:
}

Functions

func add(a int, b int) int {
    return a + b
}

Multiple Returns

func f() (int, error) {
    return 0, nil
}

Named Returns

func f() (result int) {
    result = 10
    return
}

Pointers

var x int
p := &x
fmt.Println(*p)
  • No pointer arithmetic
  • Used for mutation & performance
  • Methods often use pointer receivers

Arrays

var a [3]int
b := [3]int{1,2,3}
  • Fixed length
  • Value types
  • Rarely used directly

Slices

s := []int{1,2,3}
s = append(s, 4)

Slice Expressions

s[low:high]

Copy

dst := make([]int, len(src))
copy(dst, src)

⚠️ Slices share backing arrays


Maps

m := map[string]int{
    "a": 1,
}

Lookup:

v, ok := m["a"]

Delete:

delete(m, "a")
  • Reference type
  • Not thread-safe
  • Iteration order is random

Structs

type User struct {
    ID   int
    Name string
}

Construction:

u := User{ID: 1, Name: "Jon"}

Anonymous:

x := struct{ A int }{A: 1}

Methods & Receivers

func (u User) Name() string {}
func (u *User) Rename(n string) {}

Rules:

  • Value receiver → copy
  • Pointer receiver → mutate
  • Be consistent

Interfaces

type Reader interface {
    Read([]byte) (int, error)
}

Implicit implementation:

type File struct{}
func (File) Read([]byte) (int, error) {}

Empty interface:

var x any

Generics

func Max[T comparable](a, b T) T {
    if a > b {
        return a
    }
    return b
}

Constraints:

type Number interface {
    ~int | ~float64
}
  • Powerful but intentionally limited
  • No specialization or inheritance

Error Handling

v, err := do()
if err != nil {
    return err
}

Custom errors:

errors.New("msg")
fmt.Errorf("msg: %w", err)

Error wrapping:

errors.Is(err, target)
errors.As(err, &target)

defer, panic, recover

defer

defer f()
  • Runs at function exit
  • LIFO order

panic

panic("fatal")

recover

defer func() {
    if r := recover(); r != nil {
    }
}()
  • Only works inside deferred functions
  • Use sparingly

Concurrency

Goroutines

go doWork()
  • Lightweight
  • Thousands are fine

⚠️ No automatic cancellation or cleanup


Channels

ch := make(chan int)

Send / Receive:

ch <- 1
x := <-ch

Buffered:

ch := make(chan int, 10)

Close:

close(ch)

Select:

select {
case v := <-ch:
default:
}

Packages & Modules

Init module:

go mod init example.com/app

Import:

import "fmt"
import "example.com/pkg"
  • One module = many packages
  • go.sum is checksums, not lockfile

Visibility & Naming

func Public()
func private()
  • Uppercase = exported
  • Lowercase = package-private

Memory & Zero Values

Zero values:

0, false, "", nil

Safe defaults:

var s []int // nil but usable

Escape analysis decides stack vs heap.


Common Gotchas

  • nil interfaces ≠ nil values
  • Loop variable capture in goroutines
  • Shadowing with :=
  • Map iteration order is random
  • JSON tags must match exactly
  • Slices share backing arrays

Idiomatic Go Rules

  • Prefer clarity over cleverness
  • Avoid deep abstraction
  • Explicit error handling
  • Small interfaces
  • Composition over inheritance
  • One obvious way to do things

TL;DR

Go is:

  • Simple
  • Explicit
  • Fast
  • Predictable

It rewards discipline and punishes cleverness.


Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment