diff --git a/patterns/api-conventions.md b/patterns/api-conventions.md index 4ee621b..7b43bee 100644 --- a/patterns/api-conventions.md +++ b/patterns/api-conventions.md @@ -1,10 +1,12 @@ # API Conventions in the Go Standard Library + +**Source:** [golang/go](https://github.com/golang/go) at commit [`17bd5ab`](https://github.com/golang/go/tree/17bd5ab8c650155dd2bd09f7005726552639eea0) ## 1. The Must Pattern **Pattern name:** MustXxx (Panic on Error) -**Source citation:** `regexp/regexp.go` lines 310–320, `text/template/helper.go` lines 19–30 +**Source citation:** [regexp/regexp.go#L310](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/regexp/regexp.go#L310), [text/template/helper.go#L19](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/text/template/helper.go#L19) **What it does:** A function wraps a fallible constructor and panics if the error is non-nil. Named `MustXxx` or `Must` (when wrapping a generic `(T, error)` pair). @@ -114,7 +116,7 @@ func Must(t *Template, err error) *Template { **Pattern name:** Fallible Constructor + Must Wrapper -**Source citation:** `regexp/regexp.go` lines 130–131, 310–320 +**Source citation:** [regexp/regexp.go#L130](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/regexp/regexp.go#L130), 310–320 **What it does:** The real constructor returns `(*T, error)`. A parallel `Must` variant wraps it for use in global variable initialization. @@ -149,7 +151,7 @@ func MustCompile(str string) *Regexp { **Pattern name:** WithContext Function Overload -**Source citation:** `net/http/request.go` lines 867–869, 894–930 +**Source citation:** [net/http/request.go#L867](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/net/http/request.go#L867), 894–930 **What it does:** Provides two function variants: `NewRequest` (uses `context.Background()`) and `NewRequestWithContext` (accepts an explicit context). The simple version delegates @@ -182,7 +184,7 @@ func NewRequestWithContext(ctx context.Context, method, url string, body io.Read **Pattern name:** `*Options` Parameter — Nil Means Defaults -**Source citation:** `log/slog/text_handler.go` lines 28–42, `log/slog/handler.go` lines 135–175 +**Source citation:** [log/slog/text_handler.go#L28](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/log/slog/text_handler.go#L28), [log/slog/handler.go#L135](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/log/slog/handler.go#L135) **What it does:** A constructor accepts a pointer to an options struct. If the pointer is nil, all defaults apply. The constructor internally substitutes a zero-value struct. @@ -221,7 +223,7 @@ func NewTextHandler(w io.Writer, opts *HandlerOptions) *TextHandler { **Pattern name:** Builder (Write Methods + String/Bytes Finalizer) -**Source citation:** `strings/builder.go` lines 14–113 +**Source citation:** [strings/builder.go#L14](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/strings/builder.go#L14) **What it does:** A zero-value struct accumulates data via Write/WriteByte/WriteString methods, then produces a final result via String(). The builder is not reusable after @@ -264,7 +266,7 @@ func (b *Builder) String() string { **Pattern name:** Convenience Wrappers over Configurable Core -**Source citation:** `os/file.go` lines 385–415 +**Source citation:** [os/file.go#L385](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/os/file.go#L385) **What it does:** Simple functions (`Open`, `Create`) delegate to the fully configurable `OpenFile` with pre-set flags. Users choose their level of control. @@ -355,7 +357,7 @@ func OpenFile(name string, flag int, perm FileMode) (*File, error) { **Pattern name:** Convenience Package Functions -**Source citation:** `net/http/client.go` line 109, implied by `http.Get`, `http.Post` +**Source citation:** [net/http/client.go#L109](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/net/http/client.go#L109), implied by `http.Get`, `http.Post` **What it does:** Top-level functions like `http.Get(url)` call methods on the `DefaultClient`. Users can bypass by creating their own `Client`. @@ -384,7 +386,7 @@ var DefaultClient = &Client{} **Pattern name:** RegisterXxx for Side-Effect Imports -**Source citation:** `crypto/crypto.go` lines 145–150 +**Source citation:** [crypto/crypto.go#L145](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/crypto/crypto.go#L145) **What it does:** A `RegisterHash(h Hash, f func() hash.Hash)` function allows algorithm implementations in sub-packages to register themselves via `init()`. @@ -415,7 +417,7 @@ func RegisterHash(h Hash, f func() hash.Hash) { **Pattern name:** Close vs Shutdown (Immediate vs Graceful) -**Source citation:** `net/http/server.go` lines 3171–3220 (Close), 3221+ (Shutdown) +**Source citation:** [net/http/server.go#L3171](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/net/http/server.go#L3171) (Close), 3221+ (Shutdown) **What it does:** Provides both `Close()` (immediate, forceful) and `Shutdown(ctx)` (graceful, waits for in-flight requests). The context on Shutdown provides a @@ -530,7 +532,7 @@ func (s *Server) Shutdown(ctx context.Context) error { **Pattern name:** NewXxx Returning Channel-Bearing Struct -**Source citation:** `time/tick.go` lines 16–45, `time/sleep.go` lines 89–155 +**Source citation:** [time/tick.go#L16](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/time/tick.go#L16), [time/sleep.go#L89](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/time/sleep.go#L89) **What it does:** `NewTicker(d)` and `NewTimer(d)` return structs with a `C <-chan Time` field. Consumers select on the channel to receive time events. @@ -561,3 +563,5 @@ func NewTicker(d Duration) *Ticker { return t } ``` + + diff --git a/patterns/concurrency.md b/patterns/concurrency.md index d9913c4..1547cd2 100644 --- a/patterns/concurrency.md +++ b/patterns/concurrency.md @@ -6,10 +6,10 @@ Patterns extracted from the Go standard library source code. ## 1. sync.Mutex — The Basic Lock -### Source: `src/sync/mutex.go:18-34`, `src/sync/mutex.go:42-67` +### Source: [src/sync/mutex.go#L18](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/sync/mutex.go#L18), [src/sync/mutex.go#L42](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/sync/mutex.go#L42) ```go -// src/sync/mutex.go:18-34 +// [src/sync/mutex.go#L18](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/sync/mutex.go#L18) // A Mutex is a mutual exclusion lock. // The zero value for a Mutex is an unlocked mutex. // @@ -19,13 +19,13 @@ type Mutex struct { mu isync.Mutex } -// src/sync/mutex.go:36-39 +// [src/sync/mutex.go#L36](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/sync/mutex.go#L36) type Locker interface { Lock() Unlock() } -// src/sync/mutex.go:43-46 +// [src/sync/mutex.go#L43](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/sync/mutex.go#L43) func (m *Mutex) Lock() { m.mu.Lock() } @@ -146,24 +146,24 @@ mu.Unlock() ## 2. sync.Once — Exactly-Once Initialization -### Source: `src/sync/once.go:12-36`, `src/sync/once.go:56-79` +### Source: [src/sync/once.go#L12](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/sync/once.go#L12), [src/sync/once.go#L56](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/sync/once.go#L56) ```go -// src/sync/once.go:12-23 +// [src/sync/once.go#L12](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/sync/once.go#L12) type Once struct { _ noCopy done atomic.Bool m Mutex } -// src/sync/once.go:56-63 +// [src/sync/once.go#L56](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/sync/once.go#L56) func (o *Once) Do(f func()) { if !o.done.Load() { o.doSlow(f) } } -// src/sync/once.go:65-72 +// [src/sync/once.go#L65](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/sync/once.go#L65) func (o *Once) doSlow(f func()) { o.m.Lock() defer o.m.Unlock() @@ -301,10 +301,10 @@ once.Do(func() { ## 3. sync.WaitGroup — Waiting for Goroutine Completion -### Source: `src/sync/waitgroup.go:14-43`, `src/sync/waitgroup.go:236-260` +### Source: [src/sync/waitgroup.go#L14](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/sync/waitgroup.go#L14), [src/sync/waitgroup.go#L236](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/sync/waitgroup.go#L236) ```go -// src/sync/waitgroup.go:14-43 +// [src/sync/waitgroup.go#L14](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/sync/waitgroup.go#L14) // Typically, a main goroutine will start tasks by calling WaitGroup.Go // and then wait for all tasks to complete by calling WaitGroup.Wait: // @@ -322,7 +322,7 @@ type WaitGroup struct { ### Go 1.25+: WaitGroup.Go ```go -// src/sync/waitgroup.go:236-260 +// [src/sync/waitgroup.go#L236](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/sync/waitgroup.go#L236) func (wg *WaitGroup) Go(f func()) { wg.Add(1) go func() { @@ -374,10 +374,10 @@ wg.Wait() ## 4. sync.Pool — Object Reuse for GC Pressure -### Source: `src/sync/pool.go:44-63` +### Source: [src/sync/pool.go#L44](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/sync/pool.go#L44) ```go -// src/sync/pool.go:44-63 +// [src/sync/pool.go#L44](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/sync/pool.go#L44) // Pool's purpose is to cache allocated but unused items for later reuse, // relieving pressure on the garbage collector. That is, it makes it easy to // build efficient, thread-safe free lists. @@ -439,15 +439,15 @@ pool.Put(buf) // still has data from last use ## 5. Channel as Done Signal (Context Pattern) -### Source: `src/context/context.go:83-100` (Done channel), `src/io/pipe.go:42-45` +### Source: [src/context/context.go#L83](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/context/context.go#L83) (Done channel), [src/io/pipe.go#L42](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/io/pipe.go#L42) ```go -// src/context/context.go:83-100 +// [src/context/context.go#L83](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/context/context.go#L83) // Done returns a channel that's closed when work done on behalf of this // context should be canceled. Done() <-chan struct{} -// src/io/pipe.go:42-45 +// [src/io/pipe.go#L42](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/io/pipe.go#L42) type pipe struct { once sync.Once done chan struct{} // closed on pipe close @@ -583,10 +583,10 @@ close(done) ## 6. Context Propagation Rules -### Source: `src/context/context.go:37-48` +### Source: [src/context/context.go#L37](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/context/context.go#L37) ```go -// src/context/context.go:37-48 +// [src/context/context.go#L37](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/context/context.go#L37) // Do not store Contexts inside a struct type; instead, pass a Context // explicitly to each function that needs it. The Context should be the first // parameter, typically named ctx: @@ -622,10 +622,10 @@ func doWork(data Data, ctx context.Context) // wrong position ## 7. Context Cancellation with Timeout -### Source: `src/net/http/server.go:4007-4050` (TimeoutHandler) +### Source: [src/net/http/server.go#L4007](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/net/http/server.go#L4007) (TimeoutHandler) ```go -// src/net/http/server.go:4011-4050 +// [src/net/http/server.go#L4011](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/net/http/server.go#L4011) func (h *timeoutHandler) ServeHTTP(w ResponseWriter, r *Request) { ctx, cancelCtx := context.WithTimeout(r.Context(), h.dt) defer cancelCtx() @@ -675,10 +675,10 @@ func longWork(ctx context.Context) { ## 8. Select with Non-Blocking Check -### Source: `src/io/pipe.go:51-60` +### Source: [src/io/pipe.go#L51](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/io/pipe.go#L51) ```go -// src/io/pipe.go:51-60 +// [src/io/pipe.go#L51](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/io/pipe.go#L51) func (p *pipe) read(b []byte) (n int, err error) { select { case <-p.done: @@ -719,17 +719,17 @@ for { ## 9. Channel Pipeline (io.Pipe) -### Source: `src/io/pipe.go:38-45`, `src/io/pipe.go:195-205` +### Source: [src/io/pipe.go#L38](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/io/pipe.go#L38), [src/io/pipe.go#L195](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/io/pipe.go#L195) ```go -// src/io/pipe.go:38-45 +// [src/io/pipe.go#L38](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/io/pipe.go#L38) type pipe struct { wrCh chan []byte // writer sends data slices rdCh chan int // reader returns bytes consumed done chan struct{} } -// src/io/pipe.go:195-205 +// [src/io/pipe.go#L195](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/io/pipe.go#L195) func Pipe() (*PipeReader, *PipeWriter) { pw := &PipeWriter{r: PipeReader{pipe: pipe{ wrCh: make(chan []byte), // unbuffered @@ -868,10 +868,10 @@ func produce() <-chan int { ## 10. Background Worker with Context Shutdown -### Source: `src/database/sql/sql.go:836-843` +### Source: [src/database/sql/sql.go#L836](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/database/sql/sql.go#L836) ```go -// src/database/sql/sql.go:836-843 +// [src/database/sql/sql.go#L836](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/database/sql/sql.go#L836) func OpenDB(c driver.Connector) *DB { ctx, cancel := context.WithCancel(context.Background()) db := &DB{ @@ -903,10 +903,10 @@ go func() { ## 11. noCopy — Preventing Value Copies -### Source: `src/sync/cond.go:120-126` +### Source: [src/sync/cond.go#L120](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/sync/cond.go#L120) ```go -// src/sync/cond.go:120-126 +// [src/sync/cond.go#L120](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/sync/cond.go#L120) type noCopy struct{} // Lock is a no-op used by -copylocks checker from `go vet`. @@ -947,3 +947,5 @@ func doWork(wg *sync.WaitGroup) { | Backpressure between producer/consumer | Unbuffered channels | | Long-lived background worker | Goroutine + context cancellation | | Prevent struct copying | Embed `noCopy` field | + + diff --git a/patterns/documentation.md b/patterns/documentation.md index a192fc8..ad03ba5 100644 --- a/patterns/documentation.md +++ b/patterns/documentation.md @@ -1,10 +1,12 @@ # Documentation Patterns in the Go Standard Library + +**Source:** [golang/go](https://github.com/golang/go) at commit [`17bd5ab`](https://github.com/golang/go/tree/17bd5ab8c650155dd2bd09f7005726552639eea0) ## 1. Package Documentation (doc.go or Package Comment) **Pattern name:** Package Doc Comment -**Source citation:** `net/http/doc.go` lines 6–30, `os/file.go` lines 5–43, `log/slog/doc.go` lines 6–30 +**Source citation:** [net/http/doc.go#L6](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/net/http/doc.go#L6), [os/file.go#L5](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/os/file.go#L5), [log/slog/doc.go#L6](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/log/slog/doc.go#L6) **What it does:** The first file in a package (by convention `doc.go`, or the main source file) starts with a `// Package xxx ...` comment that explains the package's @@ -57,7 +59,7 @@ expressed as key-value pairs. **Pattern name:** `# Heading` in Doc Comments -**Source citation:** `os/file.go` lines 37–43, `net/http/doc.go` (multiple sections) +**Source citation:** [os/file.go#L37](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/os/file.go#L37), `net/http/doc.go` (multiple sections) **What it does:** Uses `# Section Name` within the package doc comment to organize long documentation into navigable sections. @@ -84,7 +86,7 @@ too many sections (fragmenting simple docs). **Pattern name:** `// TypeName verb...` or `// FuncName verb...` -**Source citation:** `net/http/server.go` lines 64–82 (Handler), `bufio/scan.go` lines 14–27 (Scanner) +**Source citation:** [net/http/server.go#L64](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/net/http/server.go#L64) (Handler), [bufio/scan.go#L14](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/bufio/scan.go#L14) (Scanner) **What it does:** Every exported identifier's doc comment starts with the identifier name, followed by a verb phrase describing what it does or represents. @@ -125,7 +127,7 @@ the comment entirely. **Pattern name:** `[TypeName]`, `[Package.Symbol]`, `[Method]` Links -**Source citation:** `net/http/server.go` lines 65–70, `os/file.go` line 9 +**Source citation:** [net/http/server.go#L65](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/net/http/server.go#L65), [os/file.go#L9](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/os/file.go#L9) **What it does:** Doc comments use `[SymbolName]` to create hyperlinks to other identifiers. These render as clickable links on pkg.go.dev. @@ -158,7 +160,7 @@ over-linking (every mention of every type). **Pattern name:** `func ExampleXxx()` / `func ExampleType_Method()` -**Source citation:** `regexp/example_test.go` lines 13–28, `net/http/example_handle_test.go` lines 16–31 +**Source citation:** [regexp/example_test.go#L13](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/regexp/example_test.go#L13), [net/http/example_handle_test.go#L16](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/net/http/example_handle_test.go#L16) **What it does:** Functions named `Example`, `ExampleXxx`, or `ExampleType_Method` in `_test.go` files serve as both executable tests and documentation. They include @@ -278,7 +280,7 @@ func ExampleHandle() { **Pattern name:** Indented Code Blocks in Comments -**Source citation:** `os/file.go` lines 17–35, `time/time.go` lines 928–933 +**Source citation:** [os/file.go#L17](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/os/file.go#L17), [time/time.go#L928](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/time/time.go#L928) **What it does:** Doc comments include indented code snippets (4 spaces) that render as preformatted code blocks in godoc. @@ -318,7 +320,7 @@ for inline (use Example functions instead); examples that reference unexported s **Pattern name:** `// Deprecated: ...` in Doc Comments -**Source citation:** `net/http/server.go` line 57 (ErrWriteAfterFlush), `os/file.go` lines 93–95 +**Source citation:** [net/http/server.go#L57](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/net/http/server.go#L57) (ErrWriteAfterFlush), [os/file.go#L93](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/os/file.go#L93) **What it does:** A paragraph starting with `Deprecated:` marks an identifier as deprecated and explains what to use instead. @@ -399,7 +401,7 @@ ErrWriteAfterFlush = errors.New("unused") **Pattern name:** "If there is an error, it will be of type [*XxxError]" -**Source citation:** `os/file.go` lines 388, 406 +**Source citation:** [os/file.go#L388](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/os/file.go#L388), 406 **What it does:** Functions document the concrete error type they return, enabling callers to type-assert for additional context. @@ -428,7 +430,7 @@ func Open(name string) (*File, error) { **Pattern name:** "Safe for concurrent use" / Concurrency Guarantees -**Source citation:** `net/http/transport.go` lines 79–80, `os/types.go` line 17, `regexp/regexp.go` lines 77–79 +**Source citation:** [net/http/transport.go#L79](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/net/http/transport.go#L79), [os/types.go#L17](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/os/types.go#L17), [regexp/regexp.go#L77](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/regexp/regexp.go#L77) **What it does:** Doc comments explicitly state the concurrency safety of a type or note exceptions where concurrent use is not safe. @@ -454,3 +456,5 @@ concurrent use by multiple goroutines"). // A Regexp is safe for concurrent use by multiple goroutines, // except for configuration methods, such as [Regexp.Longest]. ``` + + diff --git a/patterns/error-handling.md b/patterns/error-handling.md index 72b62a6..58200e9 100644 --- a/patterns/error-handling.md +++ b/patterns/error-handling.md @@ -6,21 +6,21 @@ Patterns extracted from the Go standard library source code. ## 1. Sentinel Errors -### Source: `src/io/io.go:40-43` (EOF), `src/errors/errors.go:81-83` (ErrUnsupported) +### Source: [src/io/io.go#L40](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/io/io.go#L40) (EOF), [src/errors/errors.go#L81](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/errors/errors.go#L81) (ErrUnsupported) ```go -// src/io/io.go:40-43 +// [src/io/io.go#L40](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/io/io.go#L40) // EOF is the error returned by Read when no more input is available. // (Read must return EOF itself, not an error wrapping EOF, // because callers will test for EOF using ==.) var EOF = errors.New("EOF") -// src/io/io.go:47-49 +// [src/io/io.go#L47](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/io/io.go#L47) var ErrUnexpectedEOF = errors.New("unexpected EOF") ``` ```go -// src/errors/errors.go:81-83 +// [src/errors/errors.go#L81](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/errors/errors.go#L81) var ErrUnsupported = New("unsupported operation") ``` @@ -132,15 +132,15 @@ func Read() error { ## 2. errors.New — Minimal Error Construction -### Source: `src/errors/errors.go:62-69` +### Source: [src/errors/errors.go#L62](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/errors/errors.go#L62) ```go -// src/errors/errors.go:62-64 +// [src/errors/errors.go#L62](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/errors/errors.go#L62) func New(text string) error { return &errorString{text} } -// src/errors/errors.go:66-69 +// [src/errors/errors.go#L66](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/errors/errors.go#L66) type errorString struct { s string } @@ -177,10 +177,10 @@ func doThing() error { ## 3. Error Wrapping with fmt.Errorf and %w -### Source: `src/fmt/errors.go:13-23`, `src/fmt/errors.go:70-80` +### Source: [src/fmt/errors.go#L13](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/fmt/errors.go#L13), [src/fmt/errors.go#L70](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/fmt/errors.go#L70) ```go -// src/fmt/errors.go:13-23 +// [src/fmt/errors.go#L13](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/fmt/errors.go#L13) // Errorf formats according to a format specifier and returns the string // as a value that satisfies error. // @@ -190,7 +190,7 @@ func doThing() error { // Unwrap method returning a []error containing all the %w operands. func Errorf(format string, a ...any) (err error) { ... } -// src/fmt/errors.go:70-80 +// [src/fmt/errors.go#L70](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/fmt/errors.go#L70) type wrapError struct { msg string err error @@ -310,10 +310,10 @@ return fmt.Errorf("internal: %w", internalErr) // now callers depend on interna ## 4. errors.Is — Checking Error Identity Through Chains -### Source: `src/errors/wrap.go:30-44` +### Source: [src/errors/wrap.go#L30](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/errors/wrap.go#L30) ```go -// src/errors/wrap.go:30-44 +// [src/errors/wrap.go#L30](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/errors/wrap.go#L30) func Is(err, target error) bool { if err == nil || target == nil { return err == target @@ -377,10 +377,10 @@ if errors.Is(err, os.ErrNotExist) { ... } // works through wrapping ## 5. errors.As — Extracting Error Types Through Chains -### Source: `src/errors/wrap.go:96-120` +### Source: [src/errors/wrap.go#L96](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/errors/wrap.go#L96) ```go -// src/errors/wrap.go:96-120 +// [src/errors/wrap.go#L96](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/errors/wrap.go#L96) func As(err error, target any) bool { if err == nil { return false @@ -406,7 +406,7 @@ if errors.As(err, &pathErr) { ### Go 1.24+: errors.AsType (generic version) -From `src/errors/errors.go:48-56` doc: +From [src/errors/errors.go#L48](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/errors/errors.go#L48) doc: ```go if perr, ok := errors.AsType[*fs.PathError](err); ok { fmt.Println(perr.Path) @@ -428,10 +428,10 @@ if errors.As(err, &pathErr) { ... } // works through wrapping ## 6. errors.Join — Multi-Error Aggregation -### Source: `src/errors/join.go:20-39` +### Source: [src/errors/join.go#L20](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/errors/join.go#L20) ```go -// src/errors/join.go:20-39 +// [src/errors/join.go#L20](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/errors/join.go#L20) func Join(errs ...error) error { n := 0 for _, err := range errs { @@ -555,7 +555,7 @@ return lastErr ## 7. Custom Is() Method — Equivalence Classes -### Source: `src/errors/wrap.go:42-44` (doc comment), `src/context/context.go:177-179` +### Source: [src/errors/wrap.go#L42](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/errors/wrap.go#L42) (doc comment), [src/context/context.go#L177](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/context/context.go#L177) From the `errors.Is` doc: ```go @@ -569,7 +569,7 @@ From the `errors.Is` doc: Real example from context: ```go -// src/context/context.go:177-179 +// [src/context/context.go#L177](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/context/context.go#L177) type deadlineExceededError struct{} func (deadlineExceededError) Error() string { return "context deadline exceeded" } @@ -594,10 +594,10 @@ func (e MyError) Is(target error) bool { ## 8. Error Wrapping in Custom Types (Unwrap pattern) -### Source: `src/encoding/json/encode.go:276-293` +### Source: [src/encoding/json/encode.go#L276](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/encoding/json/encode.go#L276) ```go -// src/encoding/json/encode.go:276-282 +// [src/encoding/json/encode.go#L276](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/encoding/json/encode.go#L276) type MarshalerError struct { Type reflect.Type Err error @@ -650,10 +650,10 @@ func (e *MyError) Error() string { return e.Err.Error() } ## 9. ErrUnsupported — Feature Detection via Errors -### Source: `src/errors/errors.go:76-83` +### Source: [src/errors/errors.go#L76](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/errors/errors.go#L76) ```go -// src/errors/errors.go:76-83 +// [src/errors/errors.go#L76](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/errors/errors.go#L76) // ErrUnsupported indicates that a requested operation cannot be performed, // because it is unsupported. // @@ -685,10 +685,10 @@ return errors.ErrUnsupported // no info about what operation or why ## 10. Error String Conventions -### Source: `src/net/http/server.go:39-56` +### Source: [src/net/http/server.go#L39](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/net/http/server.go#L39) ```go -// src/net/http/server.go:39-56 +// [src/net/http/server.go#L39](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/net/http/server.go#L39) var ( ErrHijacked = errors.New("http: connection has been hijacked") ErrContentLength = errors.New("http: wrote more than the declared Content-Length") @@ -742,3 +742,5 @@ Is this a specific, well-known condition? | Aggregate multiple errors | `errors.Join(err1, err2)` | | Make custom types traversable | Implement `Unwrap() error` | | Define error equivalence | Implement `Is(error) bool` | + + diff --git a/patterns/interfaces.md b/patterns/interfaces.md index f387905..b019b78 100644 --- a/patterns/interfaces.md +++ b/patterns/interfaces.md @@ -6,22 +6,22 @@ Patterns extracted from the Go standard library source code. ## 1. Small Interfaces (1-2 Methods) -### Source: `src/io/io.go:80-92` (Reader), `93-103` (Writer), `105-109` (Closer) +### Source: [src/io/io.go#L80](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/io/io.go#L80) (Reader), `93-103` (Writer), `105-109` (Closer) Go's most powerful interfaces have exactly **one method**: ```go -// src/io/io.go:80-92 +// [src/io/io.go#L80](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/io/io.go#L80) type Reader interface { Read(p []byte) (n int, err error) } -// src/io/io.go:93-103 +// [src/io/io.go#L93](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/io/io.go#L93) type Writer interface { Write(p []byte) (n int, err error) } -// src/io/io.go:105-109 +// [src/io/io.go#L105](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/io/io.go#L105) type Closer interface { Close() error } @@ -120,30 +120,30 @@ Large interfaces are hard to implement, hard to mock, and couple consumers to ca ## 2. Interface Composition -### Source: `src/io/io.go:131-155` +### Source: [src/io/io.go#L131](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/io/io.go#L131) Compose small interfaces into larger ones only when needed: ```go -// src/io/io.go:131-134 +// [src/io/io.go#L131](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/io/io.go#L131) type ReadWriter interface { Reader Writer } -// src/io/io.go:136-139 +// [src/io/io.go#L136](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/io/io.go#L136) type ReadCloser interface { Reader Closer } -// src/io/io.go:141-144 +// [src/io/io.go#L141](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/io/io.go#L141) type WriteCloser interface { Writer Closer } -// src/io/io.go:146-150 +// [src/io/io.go#L146](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/io/io.go#L146) type ReadWriteCloser interface { Reader Writer @@ -168,13 +168,13 @@ func processData(rw ReadWriteCloser) { ## 3. Accept Interfaces, Return Structs -### Source: `src/io/io.go:461` (LimitReader), `src/io/io.go:618` (TeeReader) +### Source: [src/io/io.go#L461](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/io/io.go#L461) (LimitReader), [src/io/io.go#L618](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/io/io.go#L618) (TeeReader) ```go // src/io/io.go:461 func LimitReader(r Reader, n int64) Reader { return &LimitedReader{r, n} } -// src/io/io.go:467-471 +// [src/io/io.go#L467](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/io/io.go#L467) type LimitedReader struct { R Reader // underlying reader N int64 // max bytes remaining @@ -182,7 +182,7 @@ type LimitedReader struct { ``` ```go -// src/io/io.go:618-620 +// [src/io/io.go#L618](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/io/io.go#L618) func TeeReader(r Reader, w Writer) Reader { return &teeReader{r, w} } @@ -272,7 +272,7 @@ func NewServer() ServerInterface // hides useful config fields ## 4. Interface Satisfaction as a Compile-Time Check -### Source: `src/io/io.go:645`, `src/net/http/server.go:4071` +### Source: [src/io/io.go#L645](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/io/io.go#L645), [src/net/http/server.go#L4071](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/net/http/server.go#L4071) ```go // src/io/io.go:645 @@ -299,10 +299,10 @@ func doSomething(w ResponseWriter) { ## 5. Interface-Based Polymorphism (sort.Interface) -### Source: `src/sort/sort.go:16-41` +### Source: [src/sort/sort.go#L16](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/sort/sort.go#L16) ```go -// src/sort/sort.go:16-41 +// [src/sort/sort.go#L16](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/sort/sort.go#L16) type Interface interface { Len() int Less(i, j int) bool @@ -328,17 +328,17 @@ Note: Since Go 1.21, `slices.SortFunc` is preferred for slices (generic + faster ## 6. The Adapter Pattern (HandlerFunc) -### Source: `src/net/http/server.go:2334-2342` +### Source: [src/net/http/server.go#L2334](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/net/http/server.go#L2334) ```go -// src/net/http/server.go:2334-2338 +// [src/net/http/server.go#L2334](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/net/http/server.go#L2334) // The HandlerFunc type is an adapter to allow the use of // ordinary functions as HTTP handlers. If f is a function // with the appropriate signature, HandlerFunc(f) is a // Handler that calls f. type HandlerFunc func(ResponseWriter, *Request) -// src/net/http/server.go:2341-2342 +// [src/net/http/server.go#L2341](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/net/http/server.go#L2341) // ServeHTTP calls f(w, r). func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) { f(w, r) @@ -436,15 +436,15 @@ func (h myHandler) ServeHTTP(w ResponseWriter, r *Request) { ## 7. Optional Interfaces (Runtime Feature Detection) -### Source: `src/net/http/server.go:165-175` (Flusher), `src/net/http/server.go:183-206` (Hijacker) +### Source: [src/net/http/server.go#L165](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/net/http/server.go#L165) (Flusher), [src/net/http/server.go#L183](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/net/http/server.go#L183) (Hijacker) ```go -// src/net/http/server.go:165-170 +// [src/net/http/server.go#L165](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/net/http/server.go#L165) type Flusher interface { Flush() } -// src/net/http/server.go:183-206 +// [src/net/http/server.go#L183](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/net/http/server.go#L183) type Hijacker interface { Hijack() (net.Conn, *bufio.ReadWriter, error) } @@ -564,10 +564,10 @@ type ResponseWriter interface { ## 8. The Stringer Interface (Convention-Based Behavior) -### Source: `src/fmt/print.go:63-66` +### Source: [src/fmt/print.go#L63](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/fmt/print.go#L63) ```go -// src/fmt/print.go:63-66 +// [src/fmt/print.go#L63](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/fmt/print.go#L63) type Stringer interface { String() string } @@ -596,10 +596,10 @@ func printThing(v any) string { ## 9. Interface Upgrade Pattern (WriterTo/ReaderFrom in io.Copy) -### Source: `src/io/io.go:410-417` +### Source: [src/io/io.go#L410](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/io/io.go#L410) ```go -// src/io/io.go:410-417 +// [src/io/io.go#L410](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/io/io.go#L410) func copyBuffer(dst Writer, src Reader, buf []byte) (written int64, err error) { // If the reader has a WriteTo method, use it to do the copy. // Avoids an allocation and a copy. @@ -632,15 +632,15 @@ func Copy(dst Writer, src Reader) { ## 10. The driver.Driver Pattern (Plugin Interfaces) -### Source: `src/database/sql/driver/driver.go:85-97`, `104-112` +### Source: [src/database/sql/driver/driver.go#L85](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/database/sql/driver/driver.go#L85), `104-112` ```go -// src/database/sql/driver/driver.go:85-97 +// [src/database/sql/driver/driver.go#L85](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/database/sql/driver/driver.go#L85) type Driver interface { Open(name string) (Conn, error) } -// src/database/sql/driver/driver.go:104-112 +// [src/database/sql/driver/driver.go#L104](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/database/sql/driver/driver.go#L104) type DriverContext interface { OpenConnector(name string) (Connector, error) } @@ -676,3 +676,5 @@ type Driver interface { | Compile-time interface checks | `var _ Interface = (*Type)(nil)` | | Runtime interface upgrade for optimization | `io.Copy` → `WriterTo`/`ReaderFrom` | | Plugin/driver interfaces start minimal | `database/sql/driver.Driver` | + + diff --git a/patterns/package-design.md b/patterns/package-design.md index 71288f6..a9db1bc 100644 --- a/patterns/package-design.md +++ b/patterns/package-design.md @@ -6,10 +6,10 @@ Patterns extracted from the Go standard library source code. ## 1. Package-Level Documentation -### Source: `src/io/io.go:5-13`, `src/sync/mutex.go:5-11`, `src/context/context.go:5-57` +### Source: [src/io/io.go#L5](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/io/io.go#L5), [src/sync/mutex.go#L5](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/sync/mutex.go#L5), [src/context/context.go#L5](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/context/context.go#L5) ```go -// src/io/io.go:5-13 +// [src/io/io.go#L5](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/io/io.go#L5) // Package io provides basic interfaces to I/O primitives. // Its primary job is to wrap existing implementations of such primitives, // such as those in package os, into shared public interfaces that @@ -22,7 +22,7 @@ package io ``` ```go -// src/sync/mutex.go:5-11 +// [src/sync/mutex.go#L5](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/sync/mutex.go#L5) // Package sync provides basic synchronization primitives such as mutual // exclusion locks. Other than the Once and WaitGroup types, most are intended // for use by low-level library routines. Higher-level synchronization is @@ -222,7 +222,7 @@ type Parser struct { ## 5. init() Functions — Use Sparingly -### Source: `src/net/http/http2.go:37` +### Source: [src/net/http/http2.go#L37](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/net/http/http2.go#L37) ```go // src/net/http/http2.go:37 @@ -379,7 +379,7 @@ srv := &http.Server{ ## 7. Constructor Pattern — NewX Functions -### Source: `src/net/http/server.go:2639`, `src/database/sql/sql.go:836` +### Source: [src/net/http/server.go#L2639](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/net/http/server.go#L2639), [src/database/sql/sql.go#L836](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/database/sql/sql.go#L836) ```go // src/net/http/server.go:2639 @@ -387,7 +387,7 @@ func NewServeMux() *ServeMux { return new(ServeMux) } -// src/database/sql/sql.go:836-843 +// [src/database/sql/sql.go#L836](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/database/sql/sql.go#L836) func OpenDB(c driver.Connector) *DB { ctx, cancel := context.WithCancel(context.Background()) db := &DB{ @@ -488,10 +488,10 @@ The user never sees `driver.Conn`. The driver never sees `sql.DB`'s pool logic. ## 10. Context Key Pattern — Type-Safe Context Values -### Source: `src/context/context.go:132-164`, `src/net/http/server.go:244-252` +### Source: [src/context/context.go#L132](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/context/context.go#L132), [src/net/http/server.go#L244](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/net/http/server.go#L244) ```go -// src/context/context.go:132-164 (from doc) +// [src/context/context.go#L132](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/context/context.go#L132) (from doc) // package user // // type key int @@ -508,7 +508,7 @@ The user never sees `driver.Conn`. The driver never sees `sql.DB`'s pool logic. ``` ```go -// src/net/http/server.go:244-252 +// [src/net/http/server.go#L244](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/net/http/server.go#L244) var ( ServerContextKey = &contextKey{"http-server"} LocalAddrContextKey = &contextKey{"local-addr"} @@ -598,10 +598,10 @@ ctx = context.WithValue(ctx, "timeout", 5*time.Second) // use function params! ## 11. Struct Tags for Codec Configuration -### Source: `src/encoding/json/tags.go:17-21`, `src/encoding/json/encode.go:101-181` +### Source: [src/encoding/json/tags.go#L17](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/encoding/json/tags.go#L17), [src/encoding/json/encode.go#L101](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/encoding/json/encode.go#L101) ```go -// src/encoding/json/tags.go:17-21 +// [src/encoding/json/tags.go#L17](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/encoding/json/tags.go#L17) func parseTag(tag string) (string, tagOptions) { tag, opt, _ := strings.Cut(tag, ",") return tag, tagOptions(opt) @@ -645,3 +645,5 @@ Struct tags are metadata for codecs. The `json` package reads `json:"..."` tags | API layers | Separate user from implementor (SPI) | | Context values | Unexported key type + typed accessors | | Configuration | Struct literals or functional options | + + diff --git a/patterns/structs.md b/patterns/structs.md index 7cc6c96..64fb295 100644 --- a/patterns/structs.md +++ b/patterns/structs.md @@ -1,10 +1,12 @@ # Struct Design Patterns in the Go Standard Library + +**Source:** [golang/go](https://github.com/golang/go) at commit [`17bd5ab`](https://github.com/golang/go/tree/17bd5ab8c650155dd2bd09f7005726552639eea0) ## 1. Zero-Value Usability **Pattern name:** Zero Value Ready -**Source citation:** `net/http/client.go` lines 31–35, `strings/builder.go` lines 14–16 +**Source citation:** [net/http/client.go#L31](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/net/http/client.go#L31), [strings/builder.go#L14](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/strings/builder.go#L14) **What it does:** Structs are designed so their zero value is immediately useful without explicit initialization. Nil fields fall back to sensible defaults at method call time. @@ -126,7 +128,7 @@ type Buffer struct { **Pattern name:** Indirection via Unexported Impl -**Source citation:** `os/types.go` lines 16–20, `os/file_unix.go` lines 59–71 +**Source citation:** [os/types.go#L16](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/os/types.go#L16), [os/file_unix.go#L59](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/os/file_unix.go#L59) **What it does:** The exported type (`File`) embeds a pointer to an unexported type (`*file`) that holds the real implementation state. Users interact only with the @@ -173,7 +175,7 @@ type file struct { **Pattern name:** NewXxx Constructor -**Source citation:** `bufio/scan.go` lines 89–96, `bufio/bufio.go` lines 50–60 +**Source citation:** [bufio/scan.go#L89](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/bufio/scan.go#L89), [bufio/bufio.go#L50](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/bufio/bufio.go#L50) **What it does:** A package-level function `NewXxx(deps) *Xxx` constructs the type with required dependencies and internal defaults that can't be expressed via zero @@ -294,7 +296,7 @@ func NewRequest(method, url string, body io.Reader) (*Request, error) { **Pattern name:** NewXxx / NewXxxSize Pair -**Source citation:** `bufio/bufio.go` lines 50, 62, 589, 607 +**Source citation:** [bufio/bufio.go#L50](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/bufio/bufio.go#L50), 62, 589, 607 **What it does:** Provides two constructors — one with defaults (`NewReader`) and one with explicit configuration (`NewReaderSize`). The default version calls the @@ -325,7 +327,7 @@ func NewWriter(w io.Writer) *Writer { **Pattern name:** Configuration Struct (Exported Fields, Nil-Means-Default) -**Source citation:** `net/http/server.go` lines 3020–3120, `crypto/tls/common.go` lines 566+, `log/slog/handler.go` lines 135–175 +**Source citation:** [net/http/server.go#L3020](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/net/http/server.go#L3020), [crypto/tls/common.go#L566](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/crypto/tls/common.go#L566)+, [log/slog/handler.go#L135](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/log/slog/handler.go#L135) **What it does:** A struct with exported, documented fields provides all configuration knobs. Nil/zero values always mean "use the default". @@ -440,7 +442,7 @@ func NewTextHandler(w io.Writer, opts *HandlerOptions) *TextHandler { **Pattern name:** Interface Abstraction for Pluggable Implementations -**Source citation:** `crypto/crypto.go` lines 180–200, `net/http/transport.go` lines 66–82 +**Source citation:** [crypto/crypto.go#L180](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/crypto/crypto.go#L180), [net/http/transport.go#L66](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/net/http/transport.go#L66) **What it does:** Core behavior is defined via an interface. The package provides a default concrete implementation, but any user type satisfying the interface @@ -485,7 +487,7 @@ type Client struct { **Pattern name:** copyCheck (Runtime Copy Detection) -**Source citation:** `strings/builder.go` lines 25–40 +**Source citation:** [strings/builder.go#L25](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/strings/builder.go#L25) **What it does:** On first mutation, the Builder records its own address. Subsequent mutations compare the current receiver address against the recorded one. If they @@ -517,7 +519,7 @@ func (b *Builder) copyCheck() { **Pattern name:** Package-Level Default Instance -**Source citation:** `net/http/client.go` line 109, `net/http/transport.go` lines 47–58 +**Source citation:** [net/http/client.go#L109](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/net/http/client.go#L109), [net/http/transport.go#L47](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/net/http/transport.go#L47) **What it does:** The package provides a pre-configured, ready-to-use instance as a package-level variable. Package-level convenience functions delegate to it. @@ -557,7 +559,7 @@ var DefaultTransport RoundTripper = &Transport{ **Pattern name:** Post-Construction Configuration via Methods -**Source citation:** `bufio/scan.go` lines 275–293 +**Source citation:** [bufio/scan.go#L275](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/bufio/scan.go#L275) **What it does:** After construction with `NewScanner`, optional configuration is applied via methods (`Split`, `Buffer`) before the first call to `Scan`. @@ -595,3 +597,5 @@ func (s *Scanner) Split(split SplitFunc) { s.split = split } ``` + + diff --git a/patterns/style.md b/patterns/style.md index 388b31f..09ffedb 100644 --- a/patterns/style.md +++ b/patterns/style.md @@ -1,5 +1,7 @@ # Code Style Patterns in the Go Standard Library + +**Source:** [golang/go](https://github.com/golang/go) at commit [`17bd5ab`](https://github.com/golang/go/tree/17bd5ab8c650155dd2bd09f7005726552639eea0) ## 1. Naming Conventions: mixedCaps (No Underscores) **Pattern name:** mixedCaps / MixedCaps @@ -34,7 +36,7 @@ const shutdownPollIntervalMax = 500 * time.Millisecond **Pattern name:** Acronym Capitalization -**Source citation:** `net/http/request.go` line 130 (`URL`), `net/http/server.go` line 3041 (`TLSConfig`), `encoding/json/stream.go` line 280 (`JSON`) +**Source citation:** [net/http/request.go#L130](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/net/http/request.go#L130) (`URL`), [net/http/server.go#L3041](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/net/http/server.go#L3041) (`TLSConfig`), [encoding/json/stream.go#L280](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/encoding/json/stream.go#L280) (`JSON`) **What it does:** Acronyms and initialisms (URL, HTTP, ID, JSON, XML, HTML, TLS, TCP) are always fully capitalized when exported, and fully lowercased when unexported. @@ -101,7 +103,7 @@ pattern.go — URL pattern matching (ServeMux routing) **Pattern name:** `var _ Interface = (*Type)(nil)` -**Source citation:** `io/io.go` line 645, `os/file.go` lines 747–750, `encoding/json/stream.go` lines 280–281 +**Source citation:** [io/io.go#L645](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/io/io.go#L645), [os/file.go#L747](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/os/file.go#L747), [encoding/json/stream.go#L280](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/encoding/json/stream.go#L280) **What it does:** A package-level `var _ InterfaceName = (*ConcreteType)(nil)` declares that the concrete type must satisfy the interface. The compiler verifies this at @@ -193,7 +195,7 @@ var _ Pusher = (*timeoutWriter)(nil) **Pattern name:** Named Returns for Documentation (and Defer) -**Source citation:** `io/io.go` lines 87, 100, 314, 387; `os/file.go` lines 140, 175 +**Source citation:** [io/io.go#L87](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/io/io.go#L87), 100, 314, 387; [os/file.go#L140](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/os/file.go#L140), 175 **What it does:** Return values are given names when the names add documentary value (clarifying which int is what) or when `defer` needs to modify the return value. @@ -239,7 +241,7 @@ func (f *File) Read(b []byte) (n int, err error) { **Pattern name:** `defer mu.Unlock()` / `defer f.Close()` -**Source citation:** `net/http/server.go` lines 3173–3174, `net/http/example_handle_test.go` lines 21–22 +**Source citation:** [net/http/server.go#L3173](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/net/http/server.go#L3173), [net/http/example_handle_test.go#L21](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/net/http/example_handle_test.go#L21) **What it does:** Resources acquired at the top of a scope are immediately deferred for cleanup. Mutexes are locked then immediately `defer Unlock()`'d. @@ -277,7 +279,7 @@ func (h *countHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { **Pattern name:** Sentinel Errors + Structured Error Types -**Source citation:** `os/error.go` lines 14–27, `os/error.go` lines 46–67 +**Source citation:** [os/error.go#L14](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/os/error.go#L14), [os/error.go#L46](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/os/error.go#L46) **What it does:** Package-level sentinel errors (`ErrNotExist`, `ErrPermission`) are declared as `var` for use with `errors.Is()`. Structured error types (`*PathError`, @@ -357,7 +359,7 @@ func (f *File) Name() string { ... } **Pattern name:** Typed Constants with iota -**Source citation:** `crypto/crypto.go` lines 70–85, `time/time.go` lines 936–943 +**Source citation:** [crypto/crypto.go#L70](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/crypto/crypto.go#L70), [time/time.go#L936](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/time/time.go#L936) **What it does:** Related constants are grouped in a `const ( ... )` block using a named type and `iota` for sequential values. Constants of the same type @@ -463,7 +465,7 @@ const ( **Pattern name:** `// guards x` Field Comments -**Source citation:** `net/http/example_handle_test.go` line 16 +**Source citation:** [net/http/example_handle_test.go#L16](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/net/http/example_handle_test.go#L16) **What it does:** When a sync primitive (mutex) protects specific fields, a brief comment documents what it guards: `mu sync.Mutex // guards n`. @@ -490,7 +492,7 @@ type countHandler struct { **Pattern name:** Named Type for Semantic Units -**Source citation:** `time/time.go` lines 915–943 +**Source citation:** [time/time.go#L915](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/time/time.go#L915) **What it does:** `Duration` is `type Duration int64` — a named type over a primitive. This gives it its own method set (`String()`, `Hours()`, `Truncate()`) and prevents @@ -611,7 +613,7 @@ checking in code that `gofmt` would modify. **Pattern name:** Grouped Imports (stdlib / external / internal) -**Source citation:** `net/http/server.go` lines 8–36 +**Source citation:** [net/http/server.go#L8](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/net/http/server.go#L8) **What it does:** Imports are organized in groups separated by blank lines: 1. Standard library @@ -641,3 +643,5 @@ import ( "golang.org/x/net/http/httpguts" ) ``` + + diff --git a/patterns/testing-advanced.md b/patterns/testing-advanced.md index 446a27a..8009ad0 100644 --- a/patterns/testing-advanced.md +++ b/patterns/testing-advanced.md @@ -1,5 +1,7 @@ # Advanced Go Testing Patterns + +**Source:** [golang/go](https://github.com/golang/go) at commit [`17bd5ab`](https://github.com/golang/go/tree/17bd5ab8c650155dd2bd09f7005726552639eea0) Patterns extracted from the Go standard library (`src/net/http/`, `src/encoding/json/`, `src/testing/`) and Kubernetes source code. --- @@ -10,7 +12,7 @@ The canonical Go test style. Every Go stdlib test file uses this pattern. ### Pattern Name: Anonymous Struct Test Table -**Source:** `/tmp/go-src/src/net/http/header_test.go` lines 17-108 +**Source:** [src/net/http/header_test.go#L17](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/net/http/header_test.go#L17) **What they do:** Define test cases as a slice of anonymous structs, iterate with a range loop. @@ -145,7 +147,7 @@ func TestHeaderWrite(t *testing.T) { ### Pattern Name: Named Table Tests with t.Run (Subtests) -**Source:** `/tmp/go-src/src/encoding/json/encode_test.go` lines 285-320, `/tmp/go-src/src/encoding/json/scanner_test.go` lines 30-50 +**Source:** [src/encoding/json/encode_test.go#L285](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/encoding/json/encode_test.go#L285), [src/encoding/json/scanner_test.go#L30](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/encoding/json/scanner_test.go#L30) **What they do:** Combine table-driven tests with `t.Run` for named subtests. Use a `CaseName` struct that captures file/line for error reporting. @@ -180,7 +182,7 @@ func TestValid(t *testing.T) { ### Pattern Name: CaseName with Caller Position Tracking -**Source:** `/tmp/go-src/src/encoding/json/internal/jsontest/testcase.go` lines 18-37 +**Source:** [src/encoding/json/internal/jsontest/testcase.go#L18](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/encoding/json/internal/jsontest/testcase.go#L18) **What they do:** Create a helper type that captures the caller's file:line at the point of test case declaration, so error messages point back to the exact test case definition. @@ -214,7 +216,7 @@ func (pos CasePos) String() string { ### Pattern Name: t.Helper() for Clean Stack Traces -**Source:** `/tmp/go-src/src/testing/testing.go` lines 1415-1435 +**Source:** [src/testing/testing.go#L1415](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/testing/testing.go#L1415) **What they do:** Call `t.Helper()` as the first line in any test utility function. This marks the function as a helper, so test failure messages report the caller's line instead of the helper's line. @@ -254,7 +256,7 @@ func run[T TBRun[T]](t T, f func(t T, mode testMode), opts ...any) { ### Pattern Name: *testing.T as First Argument to Helpers -**Source:** `/tmp/go-src/src/net/http/serve_test.go` lines 4555-4580 +**Source:** [src/net/http/serve_test.go#L4555](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/net/http/serve_test.go#L4555) **What they do:** Pass `*testing.T` (or `testing.TB`) as the first argument to test helper functions, making the dependency on the test context explicit. @@ -288,7 +290,7 @@ mustGet := func(url string, headers ...string) { ### Pattern Name: t.Cleanup for Test-Scoped Resources -**Source:** `/tmp/go-src/src/testing/testing.go` lines 1439-1468, `/tmp/go-src/src/net/http/clientserver_test.go` lines 120-127 +**Source:** [src/testing/testing.go#L1439](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/testing/testing.go#L1439), [src/net/http/clientserver_test.go#L120](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/net/http/clientserver_test.go#L120) **What they do:** Use `t.Cleanup(fn)` instead of `defer` for resource cleanup in tests. @@ -348,7 +350,7 @@ ServeFile(w, r, "testdata/file") ### Pattern Name: Golden Files with -update Flag -**Source:** `/tmp/go-src/src/cmd/gofmt/gofmt_test.go` lines 18, 113-138 +**Source:** [src/cmd/gofmt/gofmt_test.go#L18](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/cmd/gofmt/gofmt_test.go#L18), 113-138 **What they do:** Compare test output against `.golden` files. Provide a `-update` flag that regenerates golden files from current output when behavior intentionally changes. @@ -488,7 +490,7 @@ func TestRewrite(t *testing.T) { ### Pattern Name: httptest.NewRecorder for Unit-Testing Handlers -**Source:** `/tmp/go-src/src/net/http/serve_test.go` lines 387-393 +**Source:** [src/net/http/serve_test.go#L387](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/net/http/serve_test.go#L387) **What they do:** Use `httptest.NewRecorder()` to test HTTP handlers without starting a server. Captures status code, headers, and body. @@ -591,7 +593,7 @@ func TestServeMuxHandler(t *testing.T) { ### Pattern Name: httptest.NewServer for Integration-Style Tests -**Source:** `/tmp/go-src/src/net/http/clientserver_test.go` lines 203-280 +**Source:** [src/net/http/clientserver_test.go#L203](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/net/http/clientserver_test.go#L203) **What they do:** Use `httptest.NewServer` / `httptest.NewUnstartedServer` for end-to-end HTTP testing with a real TCP listener on localhost. @@ -622,7 +624,7 @@ func newClientServerTest(t testing.TB, mode testMode, h Handler, opts ...any) *c ### Pattern Name: b.ReportAllocs + b.RunParallel + b.SetBytes -**Source:** `/tmp/go-src/src/encoding/json/bench_test.go` lines 85-101 +**Source:** [src/encoding/json/bench_test.go#L85](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/encoding/json/bench_test.go#L85) **What they do:** Combine `b.ReportAllocs()` for allocation reporting, `b.RunParallel` for concurrent benchmarks, and `b.SetBytes` for throughput metrics. @@ -660,7 +662,7 @@ func BenchmarkCodeEncoder(b *testing.B) { ### Pattern Name: testing.Short() for Expensive Tests -**Source:** `/tmp/go-src/src/net/http/serve_test.go` lines 800, 1000, 2212, 2581 +**Source:** [src/net/http/serve_test.go#L800](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/net/http/serve_test.go#L800), 1000, 2212, 2581 **What they do:** Skip slow/flaky/network-dependent tests with `testing.Short()`. The Go CI runs with `-short` in fast mode, full tests in thorough mode. @@ -754,7 +756,7 @@ func afterTest(t testing.TB) { ### Pattern Name: Bridge File for Internal Testing -**Source:** `/tmp/go-src/src/net/http/export_test.go` lines 1-50 +**Source:** [src/net/http/export_test.go#L1](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/net/http/export_test.go#L1) **What they do:** Create an `export_test.go` file in the package itself (package `http`, not `http_test`) that exports internal symbols to external test packages. Only compiled during testing. @@ -779,7 +781,7 @@ var ( ### Pattern Name: Generic Test Runner Across Protocol Modes -**Source:** `/tmp/go-src/src/net/http/clientserver_test.go` lines 100-134 +**Source:** [src/net/http/clientserver_test.go#L100](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/net/http/clientserver_test.go#L100) **What they do:** A generic `run[T]` function that executes every client/server test in HTTP/1.1, HTTP/2, and HTTP/3 modes automatically. Tests opt into specific modes via options. @@ -810,7 +812,7 @@ func run[T TBRun[T]](t T, f func(t T, mode testMode), opts ...any) { ### Pattern Name: io.Writer Adapter for *testing.T -**Source:** `/tmp/go-src/src/net/http/clientserver_test.go` lines 337-345 +**Source:** [src/net/http/clientserver_test.go#L337](https://github.com/golang/go/blob/17bd5ab8c650155dd2bd09f7005726552639eea0/src/net/http/clientserver_test.go#L337) **What they do:** Implement `io.Writer` backed by `t.Logf`, so server error logs appear in test output (visible with `-v`, suppressed otherwise). @@ -830,3 +832,5 @@ func (w testLogWriter) Write(b []byte) (int, error) { // Usage: cst.ts.Config.ErrorLog = log.New(testLogWriter{t}, "", 0) ``` + +