Gogoroutines.go
Goroutines and WaitGroup
package main
import (
"fmt"
"sync"
"sync/atomic"
)
func main() {
var wg sync.WaitGroup
var counter atomic.Int64 // thread-safe counter
for i := 0; i < 10; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
counter.Add(1)
fmt.Printf("worker %d done\n", id)
}(i) // pass i as argument — avoids closure capture bug
}
wg.Wait()
fmt.Println("Total:", counter.Load()) // 10
// Mutex for shared non-atomic state
var mu sync.Mutex
shared := make(map[string]int)
var wg2 sync.WaitGroup
for _, word := range []string{"go", "go", "rust", "go"} {
wg2.Add(1)
go func(w string) {
defer wg2.Done()
mu.Lock()
shared[w]++
mu.Unlock()
}(word)
}
wg2.Wait()
fmt.Println(shared) // map[go:3 rust:1]
}Gochannels.go
Channels, select, and done pattern
package main
import (
"fmt"
"time"
)
func producer(done <-chan struct{}) <-chan int {
ch := make(chan int)
go func() {
defer close(ch) // close signals: no more values
for i := 0; ; i++ {
select {
case <-done:
return // cancelled
case ch <- i:
time.Sleep(50 * time.Millisecond)
}
}
}()
return ch
}
func main() {
done := make(chan struct{})
ch := producer(done)
// Read 5 values then cancel
for i := 0; i < 5; i++ {
fmt.Println(<-ch) // 0, 1, 2, 3, 4
}
close(done) // signal producer to stop
// Select with timeout
timeout := time.After(100 * time.Millisecond)
for {
select {
case v, ok := <-ch:
if !ok { fmt.Println("channel closed"); return }
fmt.Println("got:", v)
case <-timeout:
fmt.Println("timed out")
return
}
}
}Goerrors.go
Error handling: wrap, Is, As
package main
import (
"errors"
"fmt"
)
// Sentinel error for comparison with errors.Is
var ErrNotFound = errors.New("not found")
// Custom error type for errors.As
type ValidationError struct {
Field string
Message string
}
func (e *ValidationError) Error() string {
return fmt.Sprintf("validation: %s: %s", e.Field, e.Message)
}
func findUser(id int) (string, error) {
if id <= 0 {
return "", &ValidationError{Field: "id", Message: "must be positive"}
}
if id > 100 {
return "", fmt.Errorf("findUser %d: %w", id, ErrNotFound) // wrap
}
return fmt.Sprintf("user%d", id), nil
}
func main() {
_, err := findUser(999)
if errors.Is(err, ErrNotFound) { // unwraps the error chain
fmt.Println("user not found")
}
_, err2 := findUser(-1)
var ve *ValidationError
if errors.As(err2, &ve) { // extract concrete type from chain
fmt.Printf("invalid field: %s — %s\n", ve.Field, ve.Message)
}
// Multi-error (Go 1.20)
err3 := errors.Join(ErrNotFound, fmt.Errorf("timeout"))
fmt.Println(errors.Is(err3, ErrNotFound)) // true
}Gohttp_server.go
HTTP server with middleware and JSON
package main
import (
"encoding/json"
"log"
"net/http"
"time"
)
type User struct {
ID int `json:"id"`
Name string `json:"name"`
}
// Middleware: wraps http.Handler
func logging(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
next.ServeHTTP(w, r)
log.Printf("%s %s %v", r.Method, r.URL.Path, time.Since(start))
})
}
func jsonResponse(w http.ResponseWriter, status int, data any) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(status)
json.NewEncoder(w).Encode(data)
}
func userHandler(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodGet:
jsonResponse(w, http.StatusOK, User{ID: 1, Name: "Alice"})
case http.MethodPost:
var u User
if err := json.NewDecoder(r.Body).Decode(&u); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
jsonResponse(w, http.StatusCreated, u)
default:
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
}
}
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/users", userHandler)
srv := &http.Server{
Addr: ":8080",
Handler: logging(mux),
ReadTimeout: 5 * time.Second,
WriteTimeout: 10 * time.Second,
IdleTimeout: 60 * time.Second,
}
log.Fatal(srv.ListenAndServe())
}Gointerfaces.go
Interfaces and implicit satisfaction
package main
import (
"fmt"
"math"
)
// Interface: any type with these methods satisfies it — no implements keyword
type Shape interface {
Area() float64
Perimeter() float64
}
type Circle struct{ Radius float64 }
type Rect struct{ W, H float64 }
func (c Circle) Area() float64 { return math.Pi * c.Radius * c.Radius }
func (c Circle) Perimeter() float64 { return 2 * math.Pi * c.Radius }
func (r Rect) Area() float64 { return r.W * r.H }
func (r Rect) Perimeter() float64 { return 2 * (r.W + r.H) }
func printShape(s Shape) {
fmt.Printf("Area: %.2f, Perimeter: %.2f\n", s.Area(), s.Perimeter())
}
func totalArea(shapes []Shape) float64 {
total := 0.0
for _, s := range shapes { total += s.Area() }
return total
}
func main() {
shapes := []Shape{Circle{5}, Rect{4, 6}}
for _, s := range shapes { printShape(s) }
fmt.Printf("Total area: %.2f\n", totalArea(shapes))
// Type assertion
var s Shape = Circle{3}
if c, ok := s.(Circle); ok {
fmt.Println("Radius:", c.Radius)
}
// Type switch
for _, sh := range shapes {
switch v := sh.(type) {
case Circle: fmt.Println("circle r=", v.Radius)
case Rect: fmt.Println("rect", v.W, "x", v.H)
}
}
}Gogenerics.go
Generics (Go 1.18+)
package main
import "fmt"
// Map applies f to every element
func Map[T, U any](s []T, f func(T) U) []U {
result := make([]U, len(s))
for i, v := range s {
result[i] = f(v)
}
return result
}
// Filter returns elements where f returns true
func Filter[T any](s []T, f func(T) bool) []T {
var result []T
for _, v := range s {
if f(v) { result = append(result, v) }
}
return result
}
// Reduce folds to a single value
func Reduce[T, U any](s []T, init U, f func(U, T) U) U {
acc := init
for _, v := range s { acc = f(acc, v) }
return acc
}
// Number constraint
type Number interface { ~int | ~int64 | ~float64 }
func Sum[T Number](s []T) T {
var total T
for _, v := range s { total += v }
return total
}
// Generic stack
type Stack[T any] struct{ items []T }
func (s *Stack[T]) Push(v T) { s.items = append(s.items, v) }
func (s *Stack[T]) Pop() (T, bool) {
if len(s.items) == 0 { var zero T; return zero, false }
v := s.items[len(s.items)-1]
s.items = s.items[:len(s.items)-1]
return v, true
}
func main() {
nums := []int{1, 2, 3, 4, 5}
doubled := Map(nums, func(n int) int { return n * 2 })
evens := Filter(nums, func(n int) bool { return n%2 == 0 })
total := Reduce(nums, 0, func(acc, n int) int { return acc + n })
fmt.Println(doubled, evens, total) // [2 4 6 8 10] [2 4] 15
}Gocontext.go
Context: cancellation and values
package main
import (
"context"
"fmt"
"time"
)
type ctxKey string
const requestIDKey ctxKey = "requestID"
func doWork(ctx context.Context) error {
// Check cancellation at each major step
select {
case <-ctx.Done():
return ctx.Err() // context.Canceled or DeadlineExceeded
default:
}
// Simulate work
time.Sleep(10 * time.Millisecond)
// Extract value from context
if id, ok := ctx.Value(requestIDKey).(string); ok {
fmt.Println("Request ID:", id)
}
return nil
}
func main() {
// WithTimeout: auto-cancel after duration
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel() // always call — frees resources even if timeout fires
// Attach values for request-scoped data (not for optional parameters)
ctx = context.WithValue(ctx, requestIDKey, "req-abc-123")
if err := doWork(ctx); err != nil {
fmt.Println("Work failed:", err)
}
// WithCancel: manual cancellation
ctx2, cancel2 := context.WithCancel(context.Background())
go func() {
time.Sleep(50 * time.Millisecond)
cancel2() // signal cancellation
}()
<-ctx2.Done()
fmt.Println("ctx2 cancelled:", ctx2.Err())
}Goslices_maps.go
Slices, maps, and Go 1.21 stdlib additions
package main
import (
"fmt"
"maps"
"slices"
)
func main() {
// Slice operations
s := []int{5, 3, 1, 4, 2}
slices.Sort(s) // [1 2 3 4 5] — in-place
idx, found := slices.BinarySearch(s, 3)
fmt.Println(idx, found) // 2 true
s2 := slices.Clone(s) // deep copy
s2 = slices.Delete(s2, 1, 3) // remove indices 1..2
fmt.Println(s2) // [1 4 5]
// slices.Collect (Go 1.23): collect iterator to slice
// all := slices.Collect(maps.Keys(m))
// Map operations (Go 1.21)
m := map[string]int{"a": 1, "b": 2, "c": 3}
m2 := maps.Clone(m)
maps.DeleteFunc(m2, func(k string, v int) bool {
return v < 2 // delete entries where value < 2
})
fmt.Println(m2) // map[b:2 c:3]
// Check equality
fmt.Println(maps.Equal(m, maps.Clone(m))) // true
// Frequency counter — common idiom
words := []string{"go", "go", "rust", "go"}
freq := make(map[string]int)
for _, w := range words { freq[w]++ }
// Safe map read: comma-ok idiom
count, ok := freq["rust"]
fmt.Println(count, ok) // 1 true
missing, ok := freq["python"]
fmt.Println(missing, ok) // 0 false
}Goembedding.go
Struct embedding and interface composition
package main
import "fmt"
type Logger struct{ prefix string }
func (l Logger) Log(msg string) { fmt.Printf("[%s] %s\n", l.prefix, msg) }
type Server struct {
Logger // embedded: Logger's methods promoted to Server
Host string
Port int
}
func (s *Server) Start() {
s.Log(fmt.Sprintf("starting on %s:%d", s.Host, s.Port)) // promoted method
}
// Interface composition
type Reader interface { Read() string }
type Writer interface { Write(s string) }
type ReadWriter interface {
Reader
Writer
}
// Struct satisfying composed interface
type Buffer struct{ data string }
func (b *Buffer) Read() string { return b.data }
func (b *Buffer) Write(s string) { b.data += s }
func copy(src Reader, dst Writer) {
dst.Write(src.Read())
}
func main() {
s := Server{Logger: Logger{"APP"}, Host: "0.0.0.0", Port: 8080}
s.Start() // [APP] starting on 0.0.0.0:8080
s.Log("ready") // promoted: s.Logger.Log
b1 := &Buffer{data: "hello"}
b2 := &Buffer{}
copy(b1, b2)
fmt.Println(b2.Read()) // hello
}Gotesting.go
Table-driven tests and benchmarks
package math_test
import (
"testing"
"time"
)
func Add(a, b int) int { return a + b }
func Fib(n int) int {
if n <= 1 { return n }
return Fib(n-1) + Fib(n-2)
}
func TestAdd(t *testing.T) {
tests := []struct {
name string
a, b int
expected int
}{
{"positive", 1, 2, 3},
{"negative", -1, -2, -3},
{"zero", 0, 0, 0},
{"mixed", 5, -3, 2},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := Add(tt.a, tt.b)
if got != tt.expected {
t.Errorf("Add(%d,%d) = %d; want %d", tt.a, tt.b, got, tt.expected)
}
})
}
}
// Benchmark: go test -bench=.
func BenchmarkFib20(b *testing.B) {
for i := 0; i < b.N; i++ {
Fib(20)
}
}
// Parallel test
func TestAddParallel(t *testing.T) {
t.Parallel() // run alongside other parallel tests
if Add(2, 3) != 5 { t.Fatal("wrong") }
}
// Test with cleanup
func TestWithSetup(t *testing.T) {
t.Cleanup(func() { /* teardown */ })
t.Log("running")
}Goio_patterns.go
io.Reader/Writer and functional options
package main
import (
"bytes"
"fmt"
"io"
"strings"
)
// io.Reader/Writer work with any source/sink — files, network, memory
func processReader(r io.Reader) (int, error) {
buf := make([]byte, 4096)
total := 0
for {
n, err := r.Read(buf)
total += n
if err == io.EOF { break }
if err != nil { return total, err }
}
return total, nil
}
func main() {
// String as Reader
r := strings.NewReader("Hello, World!")
n, _ := processReader(r)
fmt.Println("bytes:", n) // 13
// Buffer as both Reader and Writer
var buf bytes.Buffer
fmt.Fprintf(&buf, "count: %d", 42)
io.WriteString(&buf, " and more")
fmt.Println(buf.String()) // "count: 42 and more"
// Functional options pattern
type ServerConfig struct {
host string
port int
timeout int
}
type Option func(*ServerConfig)
withHost := func(h string) Option { return func(c *ServerConfig) { c.host = h } }
withPort := func(p int) Option { return func(c *ServerConfig) { c.port = p } }
newServer := func(opts ...Option) *ServerConfig {
cfg := &ServerConfig{host: "localhost", port: 8080, timeout: 30}
for _, o := range opts { o(cfg) }
return cfg
}
srv := newServer(withHost("0.0.0.0"), withPort(9090))
fmt.Printf("%+v\n", *srv)
}Gojson_struct_tags.go
JSON encoding with struct tags
package main
import (
"encoding/json"
"fmt"
"time"
)
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email,omitempty"` // omit if zero value
Password string `json:"-"` // never serialise
CreatedAt time.Time `json:"created_at"`
Tags []string `json:"tags,omitempty"`
}
func main() {
u := User{ID: 1, Name: "Alice", CreatedAt: time.Now(), Password: "secret"}
data, _ := json.Marshal(u)
fmt.Println(string(data))
// {"id":1,"name":"Alice","created_at":"2026-..."}
// email and tags omitted (empty); password omitted (-)
// Pretty print
pretty, _ := json.MarshalIndent(u, "", " ")
fmt.Println(string(pretty))
// Decode
raw := `{"id":2,"name":"Bob","email":"bob@example.com","tags":["admin"]}`
var u2 User
json.Unmarshal([]byte(raw), &u2)
fmt.Println(u2.Name, u2.Tags) // Bob [admin]
// Decode to map when schema is unknown
var m map[string]any
json.Unmarshal([]byte(raw), &m)
fmt.Println(m["name"]) // Bob
}Godefer_panic.go
defer, panic, and recover
package main
import (
"fmt"
"os"
)
// defer: runs when function returns — cleanup, logging, unlock
func readFile(path string) error {
f, err := os.Open(path)
if err != nil { return err }
defer f.Close() // guaranteed to run — even on early return or error
// Multiple defers run LIFO (last in, first out)
defer fmt.Println("done reading")
defer fmt.Println("closing file")
// ... read from f
return nil
}
// recover: catch panics in goroutines
func safeDiv(a, b int) (result int, err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("recovered panic: %v", r)
}
}()
return a / b, nil // panics if b == 0
}
// Named return values work with defer
func withNamedReturn() (n int, err error) {
defer func() {
if err != nil {
n = -1 // can modify named return values in defer
}
}()
// ... do work
return 42, nil
}
func main() {
result, err := safeDiv(10, 0)
fmt.Println(result, err) // 0 recovered panic: runtime error: integer divide by zero
result2, _ := safeDiv(10, 2)
fmt.Println(result2) // 5
}Go reference — Go overview · Learn Go
Everything Go in one place — learning paths, reference, playground, and more.
Go Hub →