result

package
v0.14.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 26, 2025 License: MIT Imports: 4 Imported by: 1

Documentation

Overview

Package result. result provides a Rust-like Result[T] type for Go to handle operations that can fail. It replaces the traditional Go pattern of returning (T, error) with a single Result[T] that is either Ok(value) or Err(error).

Benefits over traditional (T, error) pattern:

  • Type-safe: Compiler enforces error checking before accessing values
  • Composable: Chain multiple fallible operations using Map, FlatMap, AndThen
  • Explicit: Cannot accidentally use a zero value when an error occurred
  • Functional: Enables railway-oriented programming patterns
  • Early returns: BubbleUp() enables Rust-like ? operator behavior with deferred error handling

Common use cases:

  • Database operations that can fail (queries, inserts, updates)
  • API calls that may return errors
  • File I/O operations
  • Validation logic that can fail
  • Business operations with multiple failure modes

Example - Traditional Go vs Result:

// Traditional Go
user, err := repo.FindByID(123)
if err != nil {
    return nil, err
}
// Easy to forget error check and use zero value!

// With Result
userResult := repo.FindByID(123)
// Cannot access value without explicitly handling error case

Example - Using BubbleUp() for early returns:

func ProcessOrder(orderID int) (res Result[Receipt]) {
    defer Catch(&res) // Captures panics from BubbleUp()
    order := FindOrder(orderID).BubbleUp()
    payment := ProcessPayment(order).BubbleUp()
    receipt := GenerateReceipt(payment).BubbleUp()
    return Ok(receipt)
}

Index

Constants

This section is empty.

Variables

View Source
var ErrEmptyResult = fmt.Errorf("result is error but error was nil")

ErrEmptyResult is returned when a Result is in error state but was initialized with nil error. This prevents nil error values from propagating through your application.

Functions

func Catch

func Catch[T any](res *Result[T])

Catch recovers from panics raised by BubbleUp() and populates the Result pointer. This must be deferred at the beginning of functions that use BubbleUp().

When to use:

  • ALWAYS defer this when using BubbleUp() in a function
  • Place as first defer statement to ensure it runs last
  • Pass pointer to named Result return value

Note: Panics that are not from BubbleUp() will be re-raised.

Example - Basic usage:

func DoWork() (res Result[Data]) {
    defer Catch(&res) // Must be first defer
    data := FetchData().BubbleUp()
    return Ok(data)
}

Example - With error handlers:

func GetUser(id int) (res Result[User]) {
    defer Catch(&res)
    defer CatchWith(&res, func(err error) User {
        log.Printf("Using cache fallback: %v", err)
        return GetCachedUser(id).BubbleUp()
    }, ErrDatabaseDown)
    return repo.FindUser(id)
}

func CatchErr

func CatchErr[T any](out *T, err *error)

CatchErr adapts Catch for functions returning (T, error) signature. Useful when implementing interfaces or overriding methods with traditional Go signatures.

When to use:

  • When implementing interfaces that require (T, error) returns
  • When overriding methods in generated code
  • When integrating with code that expects traditional Go error handling

Example - HTTP handler implementation:

func (h *Handler) GetUser(w http.ResponseWriter, r *http.Request) (user User, err error) {
    defer CatchErr(&user, &err)
    userID := ParseUserID(r).BubbleUp()
    return repo.FindUser(userID).BubbleUp(), nil
}

Example - Interface implementation:

func (s *Service) FetchData(ctx context.Context) (data Data, err error) {
    defer CatchErr(&data, &err)
    config := LoadConfig().BubbleUp()
    return QueryAPI(ctx, config).BubbleUp(), nil
}

func CatchWith

func CatchWith[T any](res *Result[T], handler func(error) T, when ...error)

CatchWith recovers from specific errors and applies a handler function. This enables error-specific recovery strategies similar to match expressions in Rust. Must be deferred AFTER Catch() to handle errors before they bubble up.

When to use:

  • When you want to handle specific error types with custom logic
  • When implementing fallback strategies (cache, retry, default values)
  • When you need to transform or recover from known error conditions

Note: Handler can call BubbleUp() which may succeed or propagate a new error. If no when errors specified, handler applies to ALL errors.

Example - Cache fallback on database error:

func GetUser(id int) (res Result[User]) {
    defer Catch(&res)
    defer CatchWith(&res, func(err error) User {
        return GetCachedUser(id).BubbleUp()
    }, ErrDatabaseDown)
    return repo.FindUser(id)
}

Example - Multiple error handlers:

func FetchData() (res Result[Data]) {
    defer Catch(&res)
    defer CatchWith(&res, func(err error) Data {
        return FetchFromRemote().BubbleUp()
    }, ErrCacheMiss)
    defer CatchWith(&res, func(err error) Data {
        return GetFromCache().BubbleUp()
    }, ErrDatabaseTimeout)
    return repo.QueryData()
}

Example - Handle any error:

func GetConfig() (res Result[Config]) {
    defer Catch(&res)
    defer CatchWith(&res, func(err error) Config {
        log.Printf("Using defaults: %v", err)
        return DefaultConfig()
    }) // No when errors = handles all errors
    return LoadConfigFile()
}

func Fallback

func Fallback[T any](res *Result[T], fallback T, when ...error)

Fallback provides a default value when specific errors occur. Simpler alternative to CatchWith when you just need a constant fallback.

When to use:

  • When you have a simple default value for error cases
  • When you don't need custom error handling logic
  • When the fallback doesn't require computation

Note: If no when errors specified, fallback applies to ALL errors.

Example - Configuration with defaults:

func GetTimeout() (res Result[int]) {
    defer Catch(&res)
    defer Fallback(&res, 30, ErrConfigMissing, ErrInvalidConfig)
    return LoadTimeoutConfig()
}

Example - User data with guest fallback:

func GetUserName(id int) (res Result[string]) {
    defer Catch(&res)
    defer Fallback(&res, "Guest", ErrUserNotFound)
    user := repo.FindUser(id).BubbleUp()
    return Ok(user.Name)
}

Example - Fallback for any error:

func GetFeatureFlag(name string) (res Result[bool]) {
    defer Catch(&res)
    defer Fallback(&res, false) // Default to false for any error
    return config.GetFlag(name)
}

func If

func If[Out, T any](r Result[T], okFn func(T) Out, errFn func(error) Out) Out

If applies okFn if Result is Ok, otherwise applies errFn. This is a functional-style conditional that transforms Result into a non-Result type.

When to use:

  • When you need to transform Result into a different type
  • When both success and error cases need the same return type
  • When building functional pipelines

Example - HTTP status code from Result:

func GetStatusCode(result Result[User]) int {
    return result.If(
        result,
        func(u User) int { return 200 },
        func(e error) int {
            if errors.Is(e, ErrNotFound) {
                return 404
            }
            return 500
        },
    )
}

Example - Response message from Result:

func FormatResponse(result Result[Order]) string {
    return result.If(
        result,
        func(o Order) string {
            return fmt.Sprintf("Order %d created successfully", o.ID)
        },
        func(e error) string {
            return fmt.Sprintf("Order creation failed: %v", e)
        },
    )
}

func WrapFunc

func WrapFunc[T any](fn func() (T, error)) func() Result[T]

WrapFunc wraps a function returning (T, error) into a function returning Result[T]. Use to adapt existing functions to Result patterns.

When to use:

  • When you want to create reusable Result-based versions of existing zero-argument functions
  • When building higher-level APIs with Result patterns
  • When composing multiple traditional functions together

Example - Creating Result-based API clients:

var loadConfig = result.WrapFunc(config.LoadConfig)
// Now loadConfig() returns Result[Config] instead of (Config, error)

Example - Wrapping a no-arg database connection:

var connectDB = result.WrapFunc(db.Connect)

// Usage:
func Initialize() Result[Database] {
    config := loadConfig()        // Result[Config]
    db := result.AndThen(config, func(c Config) Result[Database] {
        return connectDB()        // Result[Database]
    })
    return db
}

func WrapFunc1

func WrapFunc1[A, T any](fn func(A) (T, error)) func(A) Result[T]

WrapFunc1 wraps a single-argument function returning (T, error) into a function returning Result[T].

When to use:

  • When adapting single-argument functions to Result patterns
  • When creating reusable adapters for common operations

Example - Database operation adapters:

var findUserByID = result.WrapFunc1(db.FindUserByID)
var findOrgByName = result.WrapFunc1(db.FindOrgByName)

// Usage:
userResult := findUserByID(123)
orgResult := findOrgByName("acme")

func WrapPtrFunc

func WrapPtrFunc[T any](fn func() (*T, error)) func() Result[*T]

WrapPtrFunc wraps a zero-argument function returning (*T, error) into a function returning Result[*T], treating nil pointers as errors.

When to use:

  • When you have a function like func() (*User, error) and want a Result-based version
  • When you want to reuse the adapter across multiple call sites
  • When building railway-oriented pipelines with lookup functions

Example:

var findUser = result.WrapPtrFunc(db.FindUserByID)
// Now findUser() returns Result[*User], not (*User, error)

func ProcessUser(userID int) Result[Profile] {
    user := findUser() // Result[*User]
    return user.AndThen(func(u *User) Result[Profile] {
        return findProfile(u.ID) // another Result-returning function
    })
}

func WrapPtrFunc1

func WrapPtrFunc1[A, T any](fn func(A) (*T, error)) func(A) Result[*T]

WrapPtrFunc1 wraps a single-argument function returning (*T, error) into a function returning Result[*T], treating nil pointers as errors.

When to use:

  • When you have a function like func(id int) (*User, error) and want a Result-based version
  • When you want to reuse the adapter across multiple call sites
  • When chaining multiple pointer-returning lookups

Example:

var findUser = result.WrapPtrFunc1(db.FindUserByID)
var findProfile = result.WrapPtrFunc1(db.FindProfileByUserID)

func GetCompleteUser(userID int) Result[CompleteUser] {
    return findUser(userID).
        AndThen(func(u *User) Result[Profile] {
            return findProfile(u.ID)
        }).
        Map2(func(u *User, p *Profile) CompleteUser {
            return CompleteUser{User: u, Profile: p}
        })
}

Types

type Result

type Result[T any] struct {
	// contains filtered or unexported fields
}

Result [T] represents the outcome of an operation that can fail. Every Result is either Ok(value) containing a successful result, or Err(error) containing an error. This eliminates the ambiguity of Go's (T, error) pattern where you must remember to check error before using T.

Key benefits:

  • Cannot access the value without handling the error case
  • No nil pointer dereferences from forgotten error checks
  • Chainable operations that short-circuit on first error
  • Clear API showing which operations can fail
  • Early return support via BubbleUp() method

func AndThen

func AndThen[T, U any](r Result[T], fn func(T) Result[U]) Result[U]

AndThen chains a Result-returning function if Ok, short-circuiting on Err. Similar to FlatMap but more explicit about sequencing operations.

When to use:

  • When you have a sequence of operations that depend on each other
  • When you want to make the chaining intent explicit
  • When building railway-oriented programming pipelines

Example - Sequential validation and processing:

func RegisterUser(req RegistrationRequest) Result[User] {
    return ValidateEmail(req.Email).
        AndThen(func(email string) Result[User] {
            return ValidatePassword(req.Password)
        }).
        AndThen(func(_ string) Result[User] {
            return CreateUserAccount(req)
        }).
        AndThen(func(user User) Result[User] {
            return SendVerificationEmail(user)
        })
}

Example - Multi-step order processing:

func ProcessOrder(orderReq OrderRequest) Result[Receipt] {
    return ValidateOrder(orderReq).
        AndThen(func(order Order) Result[Payment] {
            return ChargePayment(order)
        }).
        AndThen(func(payment Payment) Result[Receipt] {
            return GenerateReceipt(payment)
        })
}

func Err

func Err[T any](err error) Result[T]

Err creates a Result[T] representing a failed operation. Use this when an operation fails and you need to propagate the error.

When to use:

  • When an operation fails (database error, network error, etc.)
  • When validation fails
  • When business rules are violated
  • When converting from traditional (T, error) where error is not nil

Note: If you pass nil as error, accessing Err() will return ErrEmptyResult instead.

Example - Database query failure:

func (r *Repository) FindUser(email string) Result[User] {
    user := User{}
    err := r.db.QueryRow("SELECT * FROM users WHERE email = ?", email).Scan(&user)
    if err == sql.ErrNoRows {
        return Err[User](fmt.Errorf("user not found: %s", email))
    }
    if err != nil {
        return Err[User](fmt.Errorf("database error: %w", err))
    }
    return Ok(user)
}

func FlatMap

func FlatMap[T, U any](r Result[T], fn func(T) Result[U]) Result[U]

FlatMap chains a Result-returning function, flattening nested Results. Use when your transformation might fail and return a Result (prevents Result[Result[T]]).

When to use:

  • When the transformation itself can fail
  • When chaining multiple fallible operations
  • When you want errors to short-circuit the chain

Example - Chained database operations:

func GetUserPrimaryEmail(userID int) Result[string] {
    userResult := repo.FindUser(userID)
    // FlatMap prevents Result[Result[string]]
    return result.FlatMap(userResult, func(u User) Result[string] {
        return repo.FindPrimaryEmail(u.ID)
    })
}

Example - Validation chain:

func CreateUser(email, password string) Result[User] {
    emailResult := ValidateEmail(email)
    return result.FlatMap(emailResult, func(validEmail string) Result[User] {
        passwordResult := ValidatePassword(password)
        return result.FlatMap(passwordResult, func(validPass string) Result[User] {
            return repo.CreateUser(validEmail, validPass)
        })
    })
}

func Map

func Map[T, U any](r Result[T], fn func(T) U) Result[U]

Map transforms the value if Ok, leaving Err unchanged. Use this to transform successful results while automatically propagating errors.

When to use:

  • When you want to transform a successful value
  • When building transformation pipelines
  • When converting between types (e.g., DTO to entity)

Example - Transform user to DTO:

func GetUserDTO(userID int) Result[UserDTO] {
    userResult := repo.FindByID(userID)
    return result.Map(userResult, func(u User) UserDTO {
        return UserDTO{
            ID:    u.ID,
            Email: u.Email,
            Name:  u.FullName(),
        }
    })
}

Example - Extract specific field:

func GetUserEmail(userID int) Result[string] {
    userResult := repo.FindByID(userID)
    return result.Map(userResult, func(u User) string {
        return u.Email
    })
}

func Map2

func Map2[T, U, V any](r Result[T], s Result[U], fn func(T, U) V) Result[V]

Map2 combines two Results by applying fn if both are Ok, otherwise returns the first error. Use when you need to combine two independent operations.

When to use:

  • When you have two independent operations that both must succeed
  • When you want to combine results from parallel operations
  • When you need both values before proceeding

Example - Combining user and permissions:

func GetUserWithPermissions(userID int) Result[UserWithPerms] {
    userResult := repo.FindUser(userID)
    permsResult := repo.FindPermissions(userID)
    return result.Map2(userResult, permsResult, func(u User, p Permissions) UserWithPerms {
        return UserWithPerms{User: u, Permissions: p}
    })
}

Example - Validating multiple fields:

func ValidateRegistration(email, password string) Result[Registration] {
    emailResult := ValidateEmail(email)
    passwordResult := ValidatePassword(password)
    return result.Map2(emailResult, passwordResult, func(e, p string) Registration {
        return Registration{Email: e, Password: p}
    })
}

func Map3

func Map3[T, U, V, W any](r Result[T], s Result[U], t Result[V], fn func(T, U, V) W) Result[W]

Map3 combines three Results by applying fn if all are Ok, otherwise returns the first error. Use when you need to combine three independent operations.

When to use:

  • When you have three independent operations that all must succeed
  • When building complex validation with multiple checks
  • When aggregating data from multiple sources

Example - Complete user profile with multiple queries:

func GetCompleteProfile(userID int) Result[CompleteProfile] {
    userResult := repo.FindUser(userID)
    addressResult := repo.FindAddress(userID)
    prefsResult := repo.FindPreferences(userID)
    return result.Map3(userResult, addressResult, prefsResult,
        func(u User, a Address, p Preferences) CompleteProfile {
            return CompleteProfile{
                User:        u,
                Address:     a,
                Preferences: p,
            }
        })
}

Example - Multi-field validation:

func ValidateUserInput(email, password, username string) Result[ValidatedInput] {
    emailResult := ValidateEmail(email)
    passResult := ValidatePassword(password)
    usernameResult := ValidateUsername(username)
    return result.Map3(emailResult, passResult, usernameResult,
        func(e, p, u string) ValidatedInput {
            return ValidatedInput{Email: e, Password: p, Username: u}
        })
}

func Ok

func Ok[T any](value T) Result[T]

Ok wraps a successful value into a Result[T]. Use this when an operation succeeds and you have a valid result to return.

When to use:

  • When an operation completes successfully
  • When validation passes and you have a valid value
  • When converting from traditional (T, error) where error is nil

Example - Successful database query:

func (r *Repository) GetUser(id int) Result[User] {
    user, err := r.db.QueryUser(id)
    if err != nil {
        return Err[User](err)
    }
    return Ok(user) // Success case
}

func Wrap

func Wrap[T any](value T, err error) Result[T]

Wrap converts a (value, error) pair into a Result[T]. This bridges traditional Go error handling with Result patterns.

When to use:

  • When calling existing functions that return (T, error)
  • When migrating code to use Result without changing function signatures
  • When integrating with libraries that use traditional error handling

Example - Wrapping database operations:

func FindUserByID(id int) Result[*User] {
    user, err := db.QueryUser(id)
    return result.Wrap(user, err)
}

Example - Wrapping file operations:

func ReadConfigFile(path string) Result[[]byte] {
    data, err := os.ReadFile(path)
    return result.Wrap(data, err)
}

func WrapPtr

func WrapPtr[T any](value *T, err error) Result[*T]

WrapPtr converts a (pointer, error) pair into Result[*T], treating nil pointers as errors. Use when nil values represent failure conditions.

When to use:

  • When functions return (*T, error) and nil pointers indicate failure
  • When you want to enforce non-nil return values
  • When dealing with lookup operations that should find entities

Example - Wrapping entity lookups:

func FindOrganization(name string) Result[*Organization] {
    org, err := db.FindOrgByName(name)
    return result.WrapPtr(org, err)
}

Example - Wrapping cache lookups:

func GetCachedUser(userID int) Result[*User] {
    user, err := cache.GetUser(userID)
    return result.WrapPtr(user, err)
}

func (Result[T]) BubbleUp

func (r Result[T]) BubbleUp() T

BubbleUp returns the value if Ok, or panics with a tryError if Err. This enables Rust-like ? operator behavior when combined with Catch(). The panic will be recovered by Catch() and converted back to a Result.

When to use:

  • When you want early returns without verbose if-err-return patterns
  • When building sequential operations that should stop on first error
  • When the calling function uses defer Catch(&res)

IMPORTANT: The calling function MUST use defer Catch(&res) to recover the panic.

Example - Sequential operations with early returns:

func ProcessOrder(orderID int) (res Result[Receipt]) {
    defer Catch(&res)
    order := FindOrder(orderID).BubbleUp()       // Returns early if error
    payment := ProcessPayment(order).BubbleUp()  // Returns early if error
    receipt := GenerateReceipt(payment).BubbleUp() // Returns early if error
    return Ok(receipt)
}

Example - Chained database operations:

func GetUserEmail(userID int) (res Result[string]) {
    defer Catch(&res)
    user := repo.FindUser(userID).BubbleUp()
    profile := repo.FindProfile(user.ProfileID).BubbleUp()
    return Ok(profile.Email)
}

func (Result[T]) Err

func (r Result[T]) Err() error

Err returns the error if Result is in error state, otherwise returns nil. If Result was initialized as Err(nil), this returns ErrEmptyResult to prevent nil errors.

When to use:

  • When you need to access the error for logging
  • When wrapping errors for context
  • When propagating errors up the call stack

Example - Error logging with context:

func UpdateUser(user User) Result[User] {
    result := repo.Update(user)
    if result.IsErr() {
        log.Printf("Failed to update user %d: %v", user.ID, result.Err())
        return result.Err[User](fmt.Errorf("update failed: %w", result.Err()))
    }
    return result
}

func (Result[T]) Expect

func (r Result[T]) Expect(panicMsg string) T

Expect returns the value if Ok, or panics with the provided message if Err. Use ONLY in tests or when the error represents an unrecoverable programming error.

When to use:

  • In unit tests where failure indicates a test problem
  • After validation where error is impossible
  • When error represents an invariant violation

AVOID in production code paths where errors are expected business outcomes.

Example - Test assertion:

func TestCreateUser(t *testing.T) {
    result := service.CreateUser("test@example.com", "password")
    user := result.Expect("user creation should succeed in test")
    assert.Equal(t, "test@example.com", user.Email)
}

Example - Critical initialization:

func InitDatabase() {
    result := ConnectDB(config)
    db := result.Expect("database connection is required for application startup")
}

func (Result[T]) ExpectErr

func (r Result[T]) ExpectErr(panicMsg string) error

ExpectErr returns the error if Result is Err, or panics if Result is Ok. Use this in tests when you expect an operation to fail.

When to use:

  • In tests validating error conditions
  • When you need to assert that an operation failed
  • When testing validation logic

Example - Testing validation errors:

func TestInvalidEmail(t *testing.T) {
    result := ValidateEmail("invalid-email")
    err := result.ExpectErr("validation should fail for invalid email")
    assert.Contains(t, err.Error(), "invalid email")
}

Example - Testing authorization:

func TestUnauthorizedAccess(t *testing.T) {
    result := service.AccessResource(unauthorizedUser, resourceID)
    err := result.ExpectErr("should reject unauthorized access")
    assert.ErrorIs(t, err, ErrUnauthorized)
}

func (Result[T]) IsErr

func (r Result[T]) IsErr() bool

IsErr reports whether the Result contains an error. Equivalent to !IsOk() but reads more naturally when checking for failure.

When to use:

  • When early-returning on errors
  • When you want to handle errors first
  • When checking for failure is more natural than checking for success

Example - Early return on error:

func CreateOrder(userID int, items []Item) Result[Order] {
    userResult := repo.FindUser(userID)
    if userResult.IsErr() {
        return result.Err[Order](fmt.Errorf("user validation failed: %w", userResult.Err()))
    }
    // Continue with order creation...
}

func (Result[T]) IsOk

func (r Result[T]) IsOk() bool

IsOk reports whether the Result contains a successful value. Use this for explicit checking before accessing the value.

When to use:

  • When you need to branch on success/failure
  • When logging or debugging operation outcomes
  • When you need to perform different actions based on success

Example - Conditional processing based on result:

func ProcessUser(userID int) {
    userResult := repo.FindByID(userID)
    if userResult.IsOk() {
        user := userResult.Unwrap()
        SendWelcomeEmail(user)
        log.Printf("Processed user: %s", user.Email)
    } else {
        log.Printf("Failed to process user %d: %v", userID, userResult.Err())
    }
}

func (Result[T]) MapError

func (r Result[T]) MapError(fn func(e error) error) Result[T]

MapError transforms the error if Result is Err, leaving Ok values unchanged. Use this to wrap, annotate, or transform errors while preserving successful values.

When to use:

  • When adding context to errors (wrapping with fmt.Errorf)
  • When converting between error types
  • When sanitizing errors for external APIs

Example - Adding context to database errors:

func GetUser(id int) Result[User] {
    result := repo.FindByID(id)
    return result.MapError(func(err error) error {
        return fmt.Errorf("failed to get user %d: %w", id, err)
    })
}

Example - Converting internal errors to API errors:

func APIGetUser(id int) Result[User] {
    result := db.FindUser(id)
    return result.MapError(func(err error) error {
        if errors.Is(err, sql.ErrNoRows) {
            return ErrUserNotFound // Convert to API error
        }
        return ErrInternalServer // Hide internal details
    })
}

func (Result[T]) Ok

func (r Result[T]) Ok(out *T) error

Ok copies the value into out if successful, returning nil. Returns the error if Result is Err. This provides Go-idiomatic error handling.

When to use:

  • When integrating with traditional Go code expecting (value, error)
  • When you want Go's "comma ok" style extraction
  • When converting from Result back to traditional Go patterns

Example - Traditional Go error handling style:

func ProcessUser(userID int) error {
    var user User
    if err := repo.FindByID(userID).Ok(&user); err != nil {
        return fmt.Errorf("failed to load user: %w", err)
    }
    // Use user...
    return nil
}

Example - HTTP handler with Result:

func HandleGetUser(w http.ResponseWriter, r *http.Request) {
    var user User
    err := service.GetUser(userID).Ok(&user)
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }
    json.NewEncoder(w).Encode(user)
}

func (Result[T]) Unwrap

func (r Result[T]) Unwrap() T

Unwrap returns the value if Ok, or panics with a generic message if Err. Shorthand for Expect with a default panic message.

When to use:

  • In tests for quick assertions
  • When prototyping

PREFER UnwrapOr, UnwrapOrElse, or proper error handling in production.

Example - Quick test validation:

func TestFindUser(t *testing.T) {
    result := repo.FindByID(123)
    user := result.Unwrap() // Panic if error - acceptable in tests
    assert.NotEmpty(t, user.Name)
}

func (Result[T]) UnwrapOr

func (r Result[T]) UnwrapOr(defaultValue T) T

UnwrapOr returns the value if Ok, otherwise returns the provided default. This is the SAFE way to extract values with a fallback when errors occur.

When to use:

  • When you have a reasonable default value for error cases
  • When you want to continue execution despite errors
  • When the error doesn't need to be propagated

Example - Configuration with defaults:

func GetMaxConnections() int {
    result := LoadConfigValue("max_connections")
    return result.UnwrapOr(100) // Default to 100 if config load fails
}

Example - Cache with fallback:

func GetUserName(userID int) string {
    result := cache.Get(userID)
    user := result.UnwrapOr(User{Name: "Guest"})
    return user.Name
}

func (Result[T]) UnwrapOrElse

func (r Result[T]) UnwrapOrElse(fn func(error) T) T

UnwrapOrElse returns the value if Ok, otherwise computes a fallback from the error. Use when you need to generate a fallback based on the error that occurred.

When to use:

  • When the fallback depends on the error type
  • When you want to log the error and provide a default
  • When the fallback is expensive and should only be computed on error

Example - Error-specific fallback:

func LoadUserProfile(userID int) User {
    result := db.FindUser(userID)
    return result.UnwrapOrElse(func(err error) User {
        log.Printf("Failed to load user %d: %v, returning guest profile", userID, err)
        return GuestUser()
    })
}

Example - Fallback with error categorization:

func GetCachedData(key string) []byte {
    result := cache.Fetch(key)
    return result.UnwrapOrElse(func(err error) []byte {
        if errors.Is(err, ErrCacheExpired) {
            return RefreshCache(key)
        }
        return []byte{}
    })
}

func (Result[T]) Value

func (r Result[T]) Value() option.Option[T]

Value returns an Option[T] containing the value if Ok, or None if Err. Use this when you want to work with the value in an Option context.

When to use:

  • When you want to treat the success value as optional
  • When converting Result to Option (discarding error details)
  • When chaining with Option operations

Example - Converting Result to Option when error details don't matter:

func TryGetCachedUser(userID int) option.Option[User] {
    cacheResult := FetchFromCache(userID)
    return cacheResult.Value() // Some(user) if ok, None if error
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL