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.
Practice now
Study without an account
x := 42 do?Scheduled with the default Askesis timings. Progress is saved in this browser.
All 50 cards
- 1What does
x := 42do?Declaresxand initializes it, inferring the type (int); short declarations only work inside functionsAt package level, writevar x = 42. - 2Zero values in Go
0for numbers,""for strings,falsefor booleans, andnilfor pointers, slices, maps, channels, functions and interfaces - 3Go’s loop formsOnly
for: three-clausefor i := 0; i < n; i++, condition-onlyfor x < 10, infinitefor {}, andfor i, v := range xs - 4Loop over 0, 1, 2 without a three-clause
forgofor i := range 3 { fmt.Println(i) }Ranging over an integer was added in Go 1.22. - 5Loop variables since Go 1.22Each iteration gets a fresh variable, so closures and goroutines started in the loop see different valuesBefore 1.22 all iterations shared one variable, a classic bug.
- 6Does
switchfall through to the next case?No. Each case ends automatically; use thefallthroughkeyword to continue into the next oneAswitchwith no expression is a tidy replacement for an if-else chain. - 7How is a name exported from a package?By starting it with an uppercase letter:
Parseis exported,parseis not - 8
newvsmakenew(T)returns a pointer to a zeroedT;makeinitializes a slice, map or channel and returns it directly - 9Array vs sliceAn 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
lenvscapof a slicelenis how many elements the slice holds;capis how many fit in the underlying array from the slice’s start - 11Why do you write
s = append(s, x)?When capacity runs out,appendallocates a bigger array, copies the elements and returns a new slice header; the oldsstill points at the old array - 12What 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, sobsharesa’s underlying arrayUseslices.Clone(a)orcopywhen you need an independent slice. - 13Make an empty slice with room for 10 elementsgo
s := make([]int, 0, 10)Preallocating avoids repeated reallocations when you know the size. - 14Read a map key and tell “missing” from a zero valuego
v, ok := m["key"]A missing key returns the value type’s zero value;okis false. - 15What happens when you write to a nil map?A panic. Create the map first with
make(map[string]int)or a literalReading from a nil map is fine and returns zero values. - 16In what order does
rangevisit a map?An unspecified order that is deliberately randomized; sort the keys when you need a stable orderslices.Sorted(maps.Keys(m))gives sorted keys (Go 1.23+). - 17What is
len("héllo")?6:lencounts bytes, and é takes two bytes in UTF-8utf8.RuneCountInStringcounts characters (runes) instead. - 18What is a
rune?An alias forint32that holds a Unicode code point;byteis an alias foruint8Ranging over a string yields runes:for i, r := range s. - 19What does
deferdo?Schedules a function call to run when the surrounding function returns, even if it panicsTypical uses: closing files, unlocking mutexes. - 20What does this print?go
for i := 0; i < 3; i++ { defer fmt.Print(i) }210: deferred calls run last-in, first-out - 21When are a deferred call’s arguments evaluated?Immediately, at the
deferstatement, not when the call finally runsgox := 1 defer fmt.Println(x) // prints 1 x = 2 - 22How do you recover from a panic?Call
recover()inside a deferred function; it stops the panic and returns the value passed topanicCalled anywhere else,recoverreturns nil. - 23The idiomatic error check after opening a filego
f, err := os.Open(name) if err != nil { return fmt.Errorf("open %s: %w", name, err) } defer f.Close() - 24The
errorinterfacegotype error interface { Error() string } - 25
%wvs%vinfmt.Errorf%wwraps the error, keeping it in the chain forerrors.Isanderrors.As;%vonly copies its text - 26
errors.Isvserrors.Aserrors.Ischecks whether any error in the chain matches a target value likeio.EOF;errors.Asfinds one of a given type and assigns it to a variable - 27How does a type implement an interface?Implicitly, by having all of its methods; there is no
implementskeyword - 28What is
any?An alias forinterface{}(Go 1.18+): the interface every type satisfies - 29Type assertion with a safe checkgo
s, ok := v.(string)Withoutok, a failed assertion panics. - 30Branch on the dynamic type of an interface valuego
switch x := v.(type) { case int: fmt.Println("int", x) case string: fmt.Println("string", x) default: fmt.Println("something else") } - 31Why can
err != nilbe true when the function returned a nil pointer?An interface is nil only if both its type and value are nil; a nil*MyErrorstored in anerrorstill has a typeReturn a literalnilwhen there is no error. - 32Value vs pointer receiversA pointer receiver can modify the value and avoids copying it; a value receiver works on a copyIf any method needs a pointer receiver, give all of the type’s methods pointer receivers.
- 33Struct embeddingEmbedding a type promotes its fields and methods to the outer struct: composition, not inheritancego
type Admin struct { User // Admin gets User’s fields and methods Level int } - 34A generic Map functiongo
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. - 35Start a function concurrentlygo
go process(item)Goroutines are lightweight threads managed by the Go runtime and multiplexed onto OS threads. - 36What happens to running goroutines when
mainreturns?The program exits and they stop immediately, finished or not - 37Wait for a group of goroutines to finishgo
var wg sync.WaitGroup for _, url := range urls { wg.Add(1) go func() { defer wg.Done() fetch(url) }() } wg.Wait()Go 1.25 addswg.Go(func() { … }), which does theAddandDonefor you. - 38Send and receive a value on a channelgo
ch := make(chan int) go func() { ch <- 42 }() v := <-ch - 39Unbuffered vs buffered channelsAn 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 - 40How do you signal that no more values are coming?The sender calls
close(ch); afor v := range chloop then ends once the channel is drainedReceiving from a closed channel returns the zero value at once;v, ok := <-chsetsokto false. - 41What happens when you send on a closed channel?A panicClosing a nil or already-closed channel panics too, so only the sender should close.
- 42What happens when you send or receive on a nil channel?It blocks foreverSetting a channel to nil is a way to disable its case in a
select. - 43What does
selectdo?Waits on several channel operations and runs whichever is ready, picking one at random if several are; adefaultcase makes it non-blocking - 44Give up on a channel receive after two secondsgo
select { case v := <-results: fmt.Println(v) case <-time.After(2 * time.Second): fmt.Println("timeout") } - 45
chan<- intvs<-chan intchan<- intis send-only;<-chan intis receive-onlyUsed in function signatures to document and enforce how a channel is used. - 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
- 47Protect a shared counter with a mutexgo
mu.Lock() defer mu.Unlock() counter++muis async.Mutex; for a single integer,sync/atomicalso works. - 48Detect data racesbash
go test -race ./...The race detector also works withgo run -raceandgo build -race. - 49Cancel an HTTP request that takes longer than two secondsgo
ctx, cancel := context.WithTimeout(ctx, 2*time.Second) defer cancel() req, err := http.NewRequestWithContext(ctx, "GET", url, nil)Always callcancel, even when the work finishes early, to release resources. - 50Start a module and tidy its dependenciesbash
go mod init example.com/app go mod tidygo mod tidyadds missing requirements togo.modand 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.
More in Programming
JavaScript
Master the JavaScript every developer uses daily: scope, closures, equality, array methods, promises, async/await and the event loop.
50 cardsAdvanced 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 cardsTypeScript
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 cardsPython
Learn idiomatic Python fast: comprehensions, f-strings, args and kwargs, generators, decorators, context managers and the standard library.
50 cardsGit & terminal
The Git commands and shell skills developers use every day: branching, rebasing, undoing mistakes, pipes, redirects, permissions and processes.
50 cardsJava
Core Java with short snippets: equals vs ==, records, sealed types, collections, generics, exceptions, streams, lambdas and JVM basics.
50 cardsC#
C# and .NET fundamentals with short snippets: value vs reference types, properties, records, nullable reference types, LINQ, async/await and DI.
50 cardsKotlin
Kotlin fundamentals with short snippets: null safety, val vs var, data and sealed classes, extension functions, scope functions and coroutines.
50 cardsSwift
Swift fundamentals with short snippets: optionals, structs vs classes, protocols, enums with associated values, closures, ARC, async/await and SwiftUI.
50 cardsFlutter
Flutter and Dart basics: null safety, futures and streams, stateless vs stateful widgets, setState, hot reload, keys, layout and state management.
50 cardsPHP
Modern PHP 8 with short snippets: strict types, arrays, match, nullsafe and named arguments, enums, readonly, Composer, PDO and password_hash.
50 cardsRuby
Ruby fundamentals with short snippets: blocks, procs and lambdas, symbols, Enumerable, mixins, truthiness, method_missing, gems and Bundler.
50 cardsRust
Learn the Rust that the compiler keeps asking about: ownership, borrowing, lifetimes, Option and Result, traits, closures, smart pointers and async.
50 cardsC++
Modern C++ in short snippets: references vs pointers, RAII, smart pointers, move semantics, STL containers and algorithms, const, templates and UB.
50 cardsHaskell
Haskell fundamentals with short code: types and type classes, pattern matching, laziness, Maybe and Either, folds, Functor, Applicative and Monad.
50 cardsElixir
Elixir fundamentals with short snippets: immutability, pattern matching, the pipe operator, Enum vs Stream, processes, GenServer, supervisors and mix.
50 cardsx86-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 cardsRegex
Learn regular expressions for good: anchors, classes, greedy vs lazy quantifiers, groups, lookarounds, flags, replacements and the backtracking trap.
50 cards