askesis

Go essentials: 50 cards on slices, errors, interfaces and goroutines

Go (Golang) fundamentals with short, idiomatic snippets: slices vs arrays, maps, defer, error wrapping, interfaces, goroutines, channels, select and context.

ProgrammingBeginner50 cardsFree

Practice now

Study without an account

0 of 50 seen0 graduated
What does x := 42 do?

Scheduled with the default Askesis timings. Progress is saved in this browser.

All 50 cards

  1. 1
    What does x := 42 do?
    Declares x and initializes it, inferring the type (int); short declarations only work inside functions
    At package level, write var x = 42.
  2. 2
    Zero values in Go
    0 for numbers, "" for strings, false for booleans, and nil for pointers, slices, maps, channels, functions and interfaces
  3. 3
    Go’s loop forms
    Only for: three-clause for i := 0; i < n; i++, condition-only for x < 10, infinite for {}, and for i, v := range xs
  4. 4
    Loop over 0, 1, 2 without a three-clause for
    go
    for i := range 3 {
    	fmt.Println(i)
    }
    Ranging over an integer was added in Go 1.22.
  5. 5
    Loop variables since Go 1.22
    Each iteration gets a fresh variable, so closures and goroutines started in the loop see different values
    Before 1.22 all iterations shared one variable, a classic bug.
  6. 6
    Does switch fall through to the next case?
    No. Each case ends automatically; use the fallthrough keyword to continue into the next one
    A switch with no expression is a tidy replacement for an if-else chain.
  7. 7
    How is a name exported from a package?
    By starting it with an uppercase letter: Parse is exported, parse is not
  8. 8
    new vs make
    new(T) returns a pointer to a zeroed T; make initializes a slice, map or channel and returns it directly
  9. 9
    Array vs slice
    An array has a fixed length that is part of its type ([3]int) and is copied on assignment; a slice is a view (pointer, length, capacity) onto an underlying array
  10. 10
    len vs cap of a slice
    len is how many elements the slice holds; cap is how many fit in the underlying array from the slice’s start
  11. 11
    Why do you write s = append(s, x)?
    When capacity runs out, append allocates a bigger array, copies the elements and returns a new slice header; the old s still points at the old array
  12. 12
    What does this print?
    go
    a := []int{1, 2, 3}
    b := a[:2]
    b[0] = 99
    fmt.Println(a)
    [99 2 3]: slicing doesn’t copy, so b shares a’s underlying array
    Use slices.Clone(a) or copy when you need an independent slice.
  13. 13
    Make an empty slice with room for 10 elements
    go
    s := make([]int, 0, 10)
    Preallocating avoids repeated reallocations when you know the size.
  14. 14
    Read a map key and tell “missing” from a zero value
    go
    v, ok := m["key"]
    A missing key returns the value type’s zero value; ok is false.
  15. 15
    What happens when you write to a nil map?
    A panic. Create the map first with make(map[string]int) or a literal
    Reading from a nil map is fine and returns zero values.
  16. 16
    In what order does range visit a map?
    An unspecified order that is deliberately randomized; sort the keys when you need a stable order
    slices.Sorted(maps.Keys(m)) gives sorted keys (Go 1.23+).
  17. 17
    What is len("héllo")?
    6: len counts bytes, and é takes two bytes in UTF-8
    utf8.RuneCountInString counts characters (runes) instead.
  18. 18
    What is a rune?
    An alias for int32 that holds a Unicode code point; byte is an alias for uint8
    Ranging over a string yields runes: for i, r := range s.
  19. 19
    What does defer do?
    Schedules a function call to run when the surrounding function returns, even if it panics
    Typical uses: closing files, unlocking mutexes.
  20. 20
    What does this print?
    go
    for i := 0; i < 3; i++ {
    	defer fmt.Print(i)
    }
    210: deferred calls run last-in, first-out
  21. 21
    When are a deferred call’s arguments evaluated?
    Immediately, at the defer statement, not when the call finally runs
    go
    x := 1
    defer fmt.Println(x) // prints 1
    x = 2
  22. 22
    How do you recover from a panic?
    Call recover() inside a deferred function; it stops the panic and returns the value passed to panic
    Called anywhere else, recover returns nil.
  23. 23
    The idiomatic error check after opening a file
    go
    f, err := os.Open(name)
    if err != nil {
    	return fmt.Errorf("open %s: %w", name, err)
    }
    defer f.Close()
  24. 24
    The error interface
    go
    type error interface {
    	Error() string
    }
  25. 25
    %w vs %v in fmt.Errorf
    %w wraps the error, keeping it in the chain for errors.Is and errors.As; %v only copies its text
  26. 26
    errors.Is vs errors.As
    errors.Is checks whether any error in the chain matches a target value like io.EOF; errors.As finds one of a given type and assigns it to a variable
  27. 27
    How does a type implement an interface?
    Implicitly, by having all of its methods; there is no implements keyword
  28. 28
    What is any?
    An alias for interface{} (Go 1.18+): the interface every type satisfies
  29. 29
    Type assertion with a safe check
    go
    s, ok := v.(string)
    Without ok, a failed assertion panics.
  30. 30
    Branch on the dynamic type of an interface value
    go
    switch x := v.(type) {
    case int:
    	fmt.Println("int", x)
    case string:
    	fmt.Println("string", x)
    default:
    	fmt.Println("something else")
    }
  31. 31
    Why can err != nil be true when the function returned a nil pointer?
    An interface is nil only if both its type and value are nil; a nil *MyError stored in an error still has a type
    Return a literal nil when there is no error.
  32. 32
    Value vs pointer receivers
    A pointer receiver can modify the value and avoids copying it; a value receiver works on a copy
    If any method needs a pointer receiver, give all of the type’s methods pointer receivers.
  33. 33
    Struct embedding
    Embedding a type promotes its fields and methods to the outer struct: composition, not inheritance
    go
    type Admin struct {
    	User // Admin gets User’s fields and methods
    	Level int
    }
  34. 34
    A generic Map function
    go
    func Map[T, U any](s []T, f func(T) U) []U {
    	out := make([]U, 0, len(s))
    	for _, v := range s {
    		out = append(out, f(v))
    	}
    	return out
    }
    Type parameters arrived in Go 1.18.
  35. 35
    Start a function concurrently
    go
    go process(item)
    Goroutines are lightweight threads managed by the Go runtime and multiplexed onto OS threads.
  36. 36
    What happens to running goroutines when main returns?
    The program exits and they stop immediately, finished or not
  37. 37
    Wait for a group of goroutines to finish
    go
    var wg sync.WaitGroup
    for _, url := range urls {
    	wg.Add(1)
    	go func() {
    		defer wg.Done()
    		fetch(url)
    	}()
    }
    wg.Wait()
    Go 1.25 adds wg.Go(func() { … }), which does the Add and Done for you.
  38. 38
    Send and receive a value on a channel
    go
    ch := make(chan int)
    go func() { ch <- 42 }()
    v := <-ch
  39. 39
    Unbuffered vs buffered channels
    An unbuffered send blocks until a receiver takes the value; on a buffered channel (make(chan int, 10)) sends block only when the buffer is full
  40. 40
    How do you signal that no more values are coming?
    The sender calls close(ch); a for v := range ch loop then ends once the channel is drained
    Receiving from a closed channel returns the zero value at once; v, ok := <-ch sets ok to false.
  41. 41
    What happens when you send on a closed channel?
    A panic
    Closing a nil or already-closed channel panics too, so only the sender should close.
  42. 42
    What happens when you send or receive on a nil channel?
    It blocks forever
    Setting a channel to nil is a way to disable its case in a select.
  43. 43
    What does select do?
    Waits on several channel operations and runs whichever is ready, picking one at random if several are; a default case makes it non-blocking
  44. 44
    Give up on a channel receive after two seconds
    go
    select {
    case v := <-results:
    	fmt.Println(v)
    case <-time.After(2 * time.Second):
    	fmt.Println("timeout")
    }
  45. 45
    chan<- int vs <-chan int
    chan<- int is send-only; <-chan int is receive-only
    Used in function signatures to document and enforce how a channel is used.
  46. 46
    “fatal error: all goroutines are asleep - deadlock!”
    Every goroutine is blocked, e.g. sending on an unbuffered channel that nothing will ever receive from
  47. 47
    Protect a shared counter with a mutex
    go
    mu.Lock()
    defer mu.Unlock()
    counter++
    mu is a sync.Mutex; for a single integer, sync/atomic also works.
  48. 48
    Detect data races
    bash
    go test -race ./...
    The race detector also works with go run -race and go build -race.
  49. 49
    Cancel an HTTP request that takes longer than two seconds
    go
    ctx, cancel := context.WithTimeout(ctx, 2*time.Second)
    defer cancel()
    req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
    Always call cancel, even when the work finishes early, to release resources.
  50. 50
    Start a module and tidy its dependencies
    bash
    go mod init example.com/app
    go mod tidy
    go mod tidy adds missing requirements to go.mod and removes unused ones.

Frequently asked questions

Do the cards use modern Go?

Yes. They reflect current Go, including generics (1.18), per-iteration loop variables and range over integers (1.22), and WaitGroup.Go (1.25).

What is the difference between an array and a slice in Go?

An array has a fixed length that is part of its type and is copied when assigned. A slice is a lightweight view onto an array, with a length and a capacity, and is what Go code uses almost everywhere.

Should I use channels or a mutex?

Channels to pass data between goroutines or coordinate them; a mutex to protect a small piece of shared state. Both are idiomatic Go.

Intermediate

JavaScript

Master the JavaScript every developer uses daily: scope, closures, equality, array methods, promises, async/await and the event loop.

50 cards
Intermediate

Advanced JS

Fifty advanced JavaScript cards: event-loop ordering puzzles, generators and async iteration, this binding, Proxy and Reflect, modules, AbortController and coercion edge cases.

50 cards
Intermediate

TypeScript

Learn the TypeScript you use every day: narrowing, generics, keyof, mapped and conditional types, utility types, satisfies and strict mode, with code on every card.

50 cards
Beginner

Python

Learn idiomatic Python fast: comprehensions, f-strings, args and kwargs, generators, decorators, context managers and the standard library.

50 cards
Beginner

Git & terminal

The Git commands and shell skills developers use every day: branching, rebasing, undoing mistakes, pipes, redirects, permissions and processes.

50 cards
Beginner

Java

Core Java with short snippets: equals vs ==, records, sealed types, collections, generics, exceptions, streams, lambdas and JVM basics.

50 cards
Beginner

C#

C# and .NET fundamentals with short snippets: value vs reference types, properties, records, nullable reference types, LINQ, async/await and DI.

50 cards
Beginner

Kotlin

Kotlin fundamentals with short snippets: null safety, val vs var, data and sealed classes, extension functions, scope functions and coroutines.

50 cards
Beginner

Swift

Swift fundamentals with short snippets: optionals, structs vs classes, protocols, enums with associated values, closures, ARC, async/await and SwiftUI.

50 cards
Beginner

Flutter

Flutter and Dart basics: null safety, futures and streams, stateless vs stateful widgets, setState, hot reload, keys, layout and state management.

50 cards
Beginner

PHP

Modern PHP 8 with short snippets: strict types, arrays, match, nullsafe and named arguments, enums, readonly, Composer, PDO and password_hash.

50 cards
Beginner

Ruby

Ruby fundamentals with short snippets: blocks, procs and lambdas, symbols, Enumerable, mixins, truthiness, method_missing, gems and Bundler.

50 cards
Intermediate

Rust

Learn the Rust that the compiler keeps asking about: ownership, borrowing, lifetimes, Option and Result, traits, closures, smart pointers and async.

50 cards
Intermediate

C++

Modern C++ in short snippets: references vs pointers, RAII, smart pointers, move semantics, STL containers and algorithms, const, templates and UB.

50 cards
Intermediate

Haskell

Haskell fundamentals with short code: types and type classes, pattern matching, laziness, Maybe and Either, folds, Functor, Applicative and Monad.

50 cards
Intermediate

Elixir

Elixir fundamentals with short snippets: immutability, pattern matching, the pipe operator, Enum vs Stream, processes, GenServer, supervisors and mix.

50 cards
Intermediate

x86-64 assembly

x86-64 assembly in Intel syntax: registers and sub-registers, flags, mov and lea, jumps, the stack frame, the System V calling convention and Linux syscalls.

50 cards
Beginner

Regex

Learn regular expressions for good: anchors, classes, greedy vs lazy quantifiers, groups, lookarounds, flags, replacements and the backtracking trap.

50 cards