- runtime.Goexit() has too much performance overhead, and should be used only under special conditions - introduce build tags
57 lines
2.3 KiB
Go
57 lines
2.3 KiB
Go
// Package result provides a generic Expect[T] type for happy-path-oriented code.
|
|
//
|
|
// # Purpose
|
|
//
|
|
// result is a convenience tool for removing error-threading clutter from
|
|
// application logic. Instead of propagating (value, error) pairs through every
|
|
// frame, functions return Expect[T] and the caller unwraps at the boundary.
|
|
//
|
|
// # Layering rule
|
|
//
|
|
// Reusable library code (packages under pkg/) must only *return* Expect[T] —
|
|
// it must never call .Expect(), .Must(), or .Expectf() itself. Those methods
|
|
// exit the current goroutine via runtime.Goexit and are only safe inside a
|
|
// goroutine controlled by [Go] or [Run]. Calling them in a library takes
|
|
// control away from the caller and makes the package non-composable.
|
|
//
|
|
// The right split:
|
|
//
|
|
// - pkg/ functions: return Expect[T] or Expect[Nothing] — let the caller decide.
|
|
// - Application code (cmd/, HTTP handlers, …): chain .Expect() calls freely,
|
|
// protected by a defer result.Catch(&err) or a result.Run wrapper.
|
|
//
|
|
// # Intended pattern
|
|
//
|
|
// 1. Deep call stacks write for the happy path, using [Expect.Expect] or
|
|
// [Expect.Expectf] to unwrap values — exiting the goroutine on failure
|
|
// (panic by default; runtime.Goexit with -tags result_goexit) rather than
|
|
// threading error returns through every frame.
|
|
// 2. The entry point wraps the work with [Go] or [Run], which spawn the work
|
|
// in a goroutine and collect any failure as a normal Go error.
|
|
//
|
|
// Stack traces are captured at the failure site and can be retrieved from the
|
|
// collected error via [StackTrace].
|
|
//
|
|
// # Constructors
|
|
//
|
|
// Use [Ok] to wrap a success value, [Fail] to wrap an error, and [Of] to
|
|
// bridge existing (value, error) return signatures:
|
|
//
|
|
// data := result.Of(os.ReadFile("cfg.json")).Expect("read config")
|
|
//
|
|
// # Boundary pattern
|
|
//
|
|
// func run() error {
|
|
// return result.Run(func() {
|
|
// port := parsePort(cfg.Port).Expect("load config port")
|
|
// _ = port // happy path continues …
|
|
// })
|
|
// }
|
|
//
|
|
// [Go] is the typed variant — it returns Expect[T] when the closure produces
|
|
// a value. [Run] is a convenience wrapper for closures that return nothing.
|
|
//
|
|
// Genuine runtime panics (nil-pointer dereferences, index out of bounds, etc.)
|
|
// are not recovered — they still crash the program, as they should.
|
|
package result
|