6 knowledge files: rust, SOLID/DRY, ast-grep, karpathy, golang, caveman 65 chunks ingested to log/knowledge/learn/latest.jsonl Chunks on ## headings, SHA256 dedup, configurable chunk size
86 lines
4.3 KiB
Markdown
86 lines
4.3 KiB
Markdown
# Go (Golang) Skills
|
|
|
|
## Core Idioms
|
|
- Accept interfaces, return structs.
|
|
- Errors are values — check them explicitly. `if err != nil { return err }`.
|
|
- Don't panic in library code. Reserve panic for truly unrecoverable situations.
|
|
- Zero values are useful — `var m map[string]int` is nil but `var s []int` is usable.
|
|
|
|
## Error Handling
|
|
- Wrap errors with context: `fmt.Errorf("failed to open %s: %w", path, err)`.
|
|
- Sentinel errors: `var ErrNotFound = errors.New("not found")`. Check with `errors.Is(err, ErrNotFound)`.
|
|
- Custom error types: `type ValidationError struct { Field, Message string }`. Check with `errors.As()`.
|
|
- Never ignore errors: `_ = doSomething()` is a code smell. At minimum, log it.
|
|
|
|
## Concurrency
|
|
- "Don't communicate by sharing memory; share memory by communicating." — use channels.
|
|
- `go func()` launches goroutine. Always ensure goroutines terminate (context, done channel).
|
|
- `sync.WaitGroup` to wait for goroutine completion.
|
|
- `sync.Mutex` when channels are overkill (protecting a counter, map).
|
|
- `context.Context` for cancellation, timeouts, and request-scoped values. Always first parameter.
|
|
- `errgroup.Group` for parallel tasks with error propagation.
|
|
|
|
## Channel Patterns
|
|
- `ch := make(chan T)` unbuffered (synchronous). `make(chan T, n)` buffered.
|
|
- Fan-out: multiple goroutines read from one channel.
|
|
- Fan-in: multiple channels merged into one via select.
|
|
- Pipeline: chain of stages connected by channels.
|
|
- `select` with `case <-ctx.Done():` for cancellation.
|
|
- Close channels from sender side only. Never close from receiver.
|
|
|
|
## Interfaces
|
|
- Interfaces are satisfied implicitly — no `implements` keyword.
|
|
- Keep interfaces small: `io.Reader` has one method. `io.ReadWriteCloser` composes three.
|
|
- Define interfaces where they're used, not where they're implemented.
|
|
- `interface{}` (or `any`) is a code smell — prefer generics or specific interfaces.
|
|
- Type assertions: `v, ok := i.(ConcreteType)`. Type switch: `switch v := i.(type) { ... }`.
|
|
|
|
## Generics (Go 1.18+)
|
|
- `func Map[T, U any](s []T, f func(T) U) []U` — generic function.
|
|
- Constraints: `comparable`, `~int | ~float64`, custom interface constraints.
|
|
- Use generics for data structures and utility functions, not business logic.
|
|
|
|
## Project Structure
|
|
```
|
|
cmd/
|
|
myapp/main.go # entrypoint
|
|
internal/ # private packages
|
|
domain/ # business logic, no external deps
|
|
repository/ # data access
|
|
handler/ # HTTP handlers
|
|
pkg/ # public library code
|
|
```
|
|
- `internal/` enforced by Go compiler — cannot be imported outside module.
|
|
- One package per directory. Package name = directory name.
|
|
|
|
## Testing
|
|
- `func TestFoo(t *testing.T)` — test functions.
|
|
- Table-driven tests: `tests := []struct{ name string; input int; want int }{ ... }`.
|
|
- `t.Run(name, func(t *testing.T) { ... })` for subtests.
|
|
- `t.Parallel()` for concurrent test execution.
|
|
- `testify/assert` for cleaner assertions. `testify/mock` for mocking.
|
|
- `httptest.NewServer()` for HTTP integration tests.
|
|
- Benchmarks: `func BenchmarkFoo(b *testing.B) { for i := 0; i < b.N; i++ { ... } }`.
|
|
|
|
## HTTP Server
|
|
- `http.HandlerFunc` wraps functions as handlers.
|
|
- Middleware pattern: `func Logging(next http.Handler) http.Handler`.
|
|
- Use `chi` or `echo` for routing. Stdlib `http.ServeMux` improved in Go 1.22.
|
|
- Always set timeouts: `srv := &http.Server{ReadTimeout: 5*time.Second, WriteTimeout: 10*time.Second}`.
|
|
- Graceful shutdown: `signal.Notify` + `srv.Shutdown(ctx)`.
|
|
|
|
## Performance
|
|
- `pprof` for CPU/memory profiling: `go tool pprof http://localhost:6060/debug/pprof/profile`.
|
|
- `sync.Pool` for reducing GC pressure on frequently allocated objects.
|
|
- Pre-allocate slices: `make([]T, 0, expectedLen)`.
|
|
- String building: `strings.Builder` not `+` concatenation.
|
|
- Avoid interface boxing in hot paths.
|
|
|
|
## Common Gotchas
|
|
- Loop variable capture in goroutines (fixed in Go 1.22, but still common in older code).
|
|
- Nil interface vs nil pointer: `var p *MyType = nil; var i MyInterface = p; i != nil` is TRUE.
|
|
- Maps are not safe for concurrent access — use `sync.Map` or `sync.RWMutex`.
|
|
- Slice append may or may not create a new backing array — never hold stale slice references.
|
|
- `defer` evaluates arguments immediately, runs function at return.
|
|
- `init()` runs before `main()` — avoid side effects, prefer explicit initialization.
|