gust

module
v1.20.6 Latest Latest
Warning

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

Go to latest
Published: Dec 31, 2025 License: MIT

README ΒΆ

gust 🌬️

Write Go code that's as safe as Rust, as expressive as functional programming, and as fast as native Go.

A zero-dependency library that brings Rust's most powerful patterns to Go, eliminating error boilerplate, nil panics, and imperative loops.

GitHub release Go Version GoDoc CI Status Go Report Card Code Coverage License

English | δΈ­ζ–‡


🎯 What is gust?

gust is a production-ready Go library that brings Rust's most powerful patterns to Go. It transforms how you write Go code by providing:

  • Type-safe error handling with Result[T] - eliminate if err != nil boilerplate
  • Safe optional values with Option[T] - no more nil pointer panics
  • Declarative iteration with 60+ iterator methods - write data pipelines like Rust

With zero dependencies and full type safety, gust lets you write Go code that's safer, cleaner, and more expressiveβ€”without sacrificing performance.

✨ The Catch Pattern: gust's Secret Weapon

gust introduces the result.Ret + Unwrap + Catch patternβ€”a revolutionary way to handle errors in Go:

func fetchUserData(userID int) (r result.Result[string]) {
    defer r.Catch()  // One line handles ALL errors!
    user := result.Ret(db.GetUser(userID)).Unwrap()
    profile := result.Ret(api.GetProfile(user.Email)).Unwrap()
    return result.Ok(fmt.Sprintf("%s: %s", user.Name, profile.Bio))
}

One line (defer r.Catch()) eliminates all if err != nil checks. Errors automatically propagate via panic and are caught, converted to Result, and returned.

✨ Why gust?
Traditional Go With gust
❌ 15+ lines of error checks βœ… 3 lines with Catch pattern
❌ if err != nil everywhere βœ… defer r.Catch() once
❌ Nil pointer panics βœ… Compile-time safety
❌ Imperative loops βœ… Declarative pipelines
❌ Hard to compose βœ… Elegant method chaining

πŸš€ Quick Start

go get github.com/andeya/gust
Your First gust Program (with Catch Pattern)
package main

import (
    "fmt"
    "github.com/andeya/gust/result"
)

func main() {
    // Using Catch pattern - errors flow automatically!
    processValue := func(value int) (r result.Result[int]) {
        defer r.Catch()
        doubled := value * 2
        if doubled > 20 {
            return result.TryErr[int]("too large")
        }
        return result.Ok(doubled + 5)
    }

    res := processValue(10)
    if res.IsOk() {
        fmt.Println("Success:", res.Unwrap())
    } else {
        fmt.Println("Error:", res.UnwrapErr())
    }
}

Output: Success: 25


πŸ’‘ The Problem gust Solves

Before: Traditional Go Code (15+ lines, 4 error checks)
func fetchUserData(userID int) (string, error) {
    user, err := db.GetUser(userID)
    if err != nil {
        return "", fmt.Errorf("db error: %w", err)
    }
    if user == nil {
        return "", fmt.Errorf("user not found")
    }
    if user.Email == "" {
        return "", fmt.Errorf("invalid user: no email")
    }
    profile, err := api.GetProfile(user.Email)
    if err != nil {
        return "", fmt.Errorf("api error: %w", err)
    }
    if profile == nil {
        return "", fmt.Errorf("profile not found")
    }
    return fmt.Sprintf("%s: %s", user.Name, profile.Bio), nil
}

Problems:

  • ❌ 4 repetitive if err != nil checks
  • ❌ 3 nested conditionals
  • ❌ Hard to test individual steps
  • ❌ Easy to forget error handling
  • ❌ 15+ lines of boilerplate
After: With gust Catch Pattern (8 lines, 0 error checks)
import "github.com/andeya/gust/result"

func fetchUserData(userID int) (r result.Result[string]) {
    defer r.Catch()  // One line handles ALL errors!
    user := result.Ret(db.GetUser(userID)).Unwrap()
    if user == nil || user.Email == "" {
        return result.TryErr[string]("invalid user")
    }
    profile := result.Ret(api.GetProfile(user.Email)).Unwrap()
    if profile == nil {
        return result.TryErr[string]("profile not found")
    }
    return result.Ok(fmt.Sprintf("%s: %s", user.Name, profile.Bio))
}

Benefits:

  • βœ… One line error handling - defer r.Catch() handles everything
  • βœ… Linear flow - Easy to read top-to-bottom
  • βœ… Automatic propagation - Errors stop execution automatically
  • βœ… Composable - Each step is independent and testable
  • βœ… Type-safe - Compiler enforces correct error handling
  • βœ… 70% less code - From 15+ lines to 8 lines

πŸ“š Core Features

1. Result - The Catch Pattern Revolution

The Catch pattern (result.Ret + Unwrap + Catch) is gust's most powerful feature:

import "github.com/andeya/gust/result"

// Before: Traditional Go (multiple error checks)
// func readConfig(filename string) (string, error) {
//     f, err := os.Open(filename)
//     if err != nil {
//         return "", err
//     }
//     defer f.Close()
//     data, err := io.ReadAll(f)
//     if err != nil {
//         return "", err
//     }
//     return string(data), nil
// }

// After: gust Catch pattern (linear flow, no error checks)
func readConfig(filename string) (r result.Result[string]) {
    defer r.Catch()  // One line handles ALL errors!
    data := result.Ret(os.ReadFile(filename)).Unwrap()
    return result.Ok(string(data))
}

Key Methods:

  • result.Ret(T, error) - Convert (T, error) to Result[T]
  • Unwrap() - Extract value (panics if error, caught by Catch)
  • defer r.Catch() - Catch all panics and convert to Result errors
  • Map - Transform value if Ok
  • AndThen - Chain operations returning Result
  • UnwrapOr - Extract safely with default (never panics)

Real-World Use Cases:

  • API call chains
  • Database operations
  • File I/O operations
  • Data validation pipelines
2. Option - No More Nil Panics

Replace *T and (T, bool) with safe Option[T] that prevents nil pointer panics:

import "github.com/andeya/gust/option"

// Before: Traditional Go (nil checks everywhere)
// func divide(a, b float64) *float64 {
//     if b == 0 {
//         return nil
//     }
//     result := a / b
//     return &result
// }
// result := divide(10, 2)
// if result != nil {
//     fmt.Println(*result * 2)  // Risk of nil pointer panic
// }

// After: gust Option (type-safe, no nil panics)
divide := func(a, b float64) option.Option[float64] {
    if b == 0 {
        return option.None[float64]()
    }
    return option.Some(a / b)
}

quotient := divide(10, 2).
    Map(func(x float64) float64 { return x * 2 }).
    UnwrapOr(0)  // Safe: never panics

fmt.Println(quotient) // 10

Key Methods:

  • Map - Transform value if Some
  • AndThen - Chain operations returning Option
  • Filter - Conditionally filter values
  • UnwrapOr - Extract safely with default (never panics)

Real-World Use Cases:

  • Configuration reading
  • Optional function parameters
  • Map lookups
  • JSON unmarshaling
3. Iterator - Rust-like Iteration

Full Rust Iterator trait implementation with 60+ methods for declarative data processing:

import "github.com/andeya/gust/iterator"

// Before: Traditional Go (nested loops, manual error handling)
// func processNumbers(input []string) ([]int, error) {
//     var results []int
//     for _, s := range input {
//         n, err := strconv.Atoi(s)
//         if err != nil {
//             continue
//         }
//         if n > 0 {
//             results = append(results, n*2)
//         }
//     }
//     return results, nil
// }

// After: gust Iterator (declarative, type-safe, 70% less code)
input := []string{"10", "20", "invalid", "30", "0", "40"}

results := iterator.FilterMap(
    iterator.RetMap(iterator.FromSlice(input), strconv.Atoi),
    result.Result[int].Ok,
).
    Filter(func(x int) bool { return x > 0 }).
    Map(func(x int) int { return x * 2 }).
    Take(3).
    Collect()

fmt.Println(results) // [20 40 60]

Highlights:

  • πŸš€ 60+ methods from Rust's Iterator trait
  • πŸ”„ Lazy evaluation - Computations happen on-demand
  • πŸ”— Method chaining - Compose complex operations elegantly
  • πŸ”Œ Go 1.24+ integration - Works with standard iter.Seq[T]
  • 🎯 Type-safe - Compile-time guarantees
  • ⚑ Zero-cost abstractions - No performance overhead

Method Categories:

  • Constructors: FromSlice, FromRange, FromFunc, Empty, Once, Repeat
  • BitSet Iterators: FromBitSet, FromBitSetOnes, FromBitSetZeros
  • Go Integration: FromSeq, Seq, Pull (Go 1.24+ standard iterators)
  • Basic Adapters: Map, Filter, Chain, Zip, Enumerate
  • Filtering: Skip, Take, StepBy, SkipWhile, TakeWhile
  • Transforming: MapWhile, Scan, FlatMap, Flatten
  • Chunking: MapWindows, ArrayChunks, ChunkBy
  • Consumers: Collect, Fold, Reduce, Count, Sum, Product, Partition
  • Search: Find, FindMap, Position, All, Any
  • Min/Max: Max, Min, MaxBy, MinBy, MaxByKey, MinByKey
  • Double-Ended: NextBack, Rfold, Rfind, NthBack

🌟 Real-World Examples

Example 1: Data Processing Pipeline (Iterator + Result)

Before: Traditional Go (nested loops + error handling, 15+ lines)

func processUserInput(input []string) ([]int, error) {
    var results []int
    for _, s := range input {
        n, err := strconv.Atoi(s)
        if err != nil {
            continue
        }
        if n > 0 {
            results = append(results, n*2)
        }
    }
    if len(results) == 0 {
        return nil, fmt.Errorf("no valid numbers")
    }
    return results, nil
}

After: gust Iterator + Result (declarative, type-safe, 8 lines)

import (
    "github.com/andeya/gust/iterator"
    "github.com/andeya/gust/result"
    "strconv"
)

input := []string{"10", "20", "invalid", "30", "0", "40"}

results := iterator.FilterMap(
    iterator.RetMap(iterator.FromSlice(input), strconv.Atoi),
    result.Result[int].Ok,
).
    Filter(func(x int) bool { return x > 0 }).
    Map(func(x int) int { return x * 2 }).
    Take(3).
    Collect()

fmt.Println(results) // [20 40 60]

Result: 70% less code, type-safe, declarative

Example 2: API Call Chain (Catch Pattern)

Before: Traditional Go (15+ lines, 4 error checks)

func fetchUserProfile(userID int) (string, error) {
    user, err := db.GetUser(userID)
    if err != nil {
        return "", fmt.Errorf("db error: %w", err)
    }
    if user == nil || user.Email == "" {
        return "", fmt.Errorf("invalid user")
    }
    profile, err := api.GetProfile(user.Email)
    if err != nil {
        return "", fmt.Errorf("api error: %w", err)
    }
    if profile == nil {
        return "", fmt.Errorf("profile not found")
    }
    return fmt.Sprintf("%s: %s", user.Name, profile.Bio), nil
}

After: gust Catch Pattern (8 lines, 0 error checks)

import "github.com/andeya/gust/result"

func fetchUserProfile(userID int) (r result.Result[string]) {
    defer r.Catch()  // One line handles ALL errors!
    user := result.Ret(db.GetUser(userID)).Unwrap()
    if user == nil || user.Email == "" {
        return result.TryErr[string]("invalid user")
    }
    profile := result.Ret(api.GetProfile(user.Email)).Unwrap()
    if profile == nil {
        return result.TryErr[string]("profile not found")
    }
    return result.Ok(fmt.Sprintf("%s: %s", user.Name, profile.Bio))
}

// Usage
profileRes := fetchUserProfile(123)
if profileRes.IsOk() {
    fmt.Println(profileRes.Unwrap())
} else {
    fmt.Println("Error:", profileRes.UnwrapErr())
}

Result: 70% less code, linear flow, automatic error propagation

Example 3: File System Operations (Catch Pattern)

Before: Traditional Go (multiple error checks, nested conditions)

func copyDirectory(src, dst string) error {
    info, err := os.Stat(src)
    if err != nil {
        return err
    }
    if err = os.MkdirAll(dst, info.Mode()); err != nil {
        return err
    }
    entries, err := os.ReadDir(src)
    if err != nil {
        return err
    }
    for _, entry := range entries {
        srcPath := filepath.Join(src, entry.Name())
        dstPath := filepath.Join(dst, entry.Name())
        if entry.IsDir() {
            if err = copyDirectory(srcPath, dstPath); err != nil {
                return err
            }
        } else {
            if err = copyFile(srcPath, dstPath); err != nil {
                return err
            }
        }
    }
    return nil
}

After: gust Catch Pattern (linear flow, single error handler)

import (
    "github.com/andeya/gust/fileutil"
    "github.com/andeya/gust/result"
    "os"
    "path/filepath"
)

func copyDirectory(src, dst string) (r result.VoidResult) {
    defer r.Catch()  // One line handles ALL errors!
    info := result.Ret(os.Stat(src)).Unwrap()
    result.RetVoid(os.MkdirAll(dst, info.Mode())).Unwrap()
    entries := result.Ret(os.ReadDir(src)).Unwrap()
    for _, entry := range entries {
        srcPath := filepath.Join(src, entry.Name())
        dstPath := filepath.Join(dst, entry.Name())
        if entry.IsDir() {
            copyDirectory(srcPath, dstPath).Unwrap()
        } else {
            fileutil.CopyFile(srcPath, dstPath).Unwrap()
        }
    }
    return result.OkVoid()
}

Result: Linear code flow, automatic error propagation, 70% less code

Example 4: Configuration Management (Option)

Before: Traditional Go (nil checks, error handling)

type Config struct {
    APIKey *string
    Port   int
}

func loadConfig() (Config, error) {
    apiKeyEnv := os.Getenv("API_KEY")
    var apiKey *string
    if apiKeyEnv != "" {
        apiKey = &apiKeyEnv
    }
    portStr := os.Getenv("PORT")
    port := 8080
    if portStr != "" {
        p, err := strconv.Atoi(portStr)
        if err != nil {
            return Config{}, err
        }
        port = p
    }
    return Config{APIKey: apiKey, Port: port}, nil
}

After: gust Option (type-safe, no nil checks)

import (
    "github.com/andeya/gust/option"
    "os"
    "strconv"
)

type Config struct {
    APIKey option.Option[string]
    Port   option.Option[int]
}

func loadConfig() Config {
    apiKeyEnv := os.Getenv("API_KEY")
    var apiKeyPtr *string
    if apiKeyEnv != "" {
        apiKeyPtr = &apiKeyEnv
    }
    return Config{
        APIKey: option.ElemOpt(apiKeyPtr),
        Port:   option.RetOpt(strconv.Atoi(os.Getenv("PORT"))),
    }
}

config := loadConfig()
port := config.Port.UnwrapOr(8080)   // Default to 8080 if not set
apiKey := config.APIKey.UnwrapOr("") // Default to empty string

Result: Type-safe, no nil checks, elegant defaults


πŸ“¦ Complete Package Ecosystem

gust provides a comprehensive set of utility packages for common Go tasks:

Package Description Key Features
gust/result Type-safe error handling Result[T], Catch pattern, Map, AndThen
gust/option Safe optional values Option[T], Map, Filter, AndThen
gust/iterator Rust-like iteration 60+ methods, lazy evaluation, method chaining
gust/dict Generic map utilities Filter, Map, Keys, Values, Get
gust/vec Generic slice utilities MapAlone, Get, Copy, Dict
gust/conv Type-safe conversions BytesToString, StringToReadonlyBytes, case conversion, JSON quoting
gust/digit Number conversions Base 2-62 conversion, FormatByDict, ParseByDict
gust/random Secure random strings Base36/Base62 encoding, timestamp embedding
gust/encrypt Cryptographic functions MD5, SHA series, FNV, CRC, Adler-32, AES encryption
gust/bitset Thread-safe bit sets Bitwise ops, iterator integration, multiple encodings
gust/syncutil Concurrent utilities SyncMap, Lazy, mutex wrappers
gust/errutil Error utilities Stack traces, panic recovery, ErrBox
gust/constraints Type constraints Ordering, Numeric, Digit
gust/fileutil File operations Path manipulation, file I/O, directory operations, tar.gz archiving

🎯 Why Choose gust?

Zero Dependencies

gust has zero external dependencies. It only uses Go's standard library, keeping your project lean and secure.

Production Ready
  • βœ… Comprehensive test coverage
  • βœ… Full documentation with examples
  • βœ… Battle-tested in production
  • βœ… Active maintenance and support
Type Safety

All operations are type-safe with compile-time guarantees. The Go compiler enforces correct usage.

Performance

gust uses zero-cost abstractions. There's no runtime overhead compared to traditional Go code.

Go 1.24+ Integration

Seamlessly works with Go 1.24+'s standard iter.Seq[T] iterators, bridging the gap between gust and standard Go.

Community
  • πŸ“– Complete API documentation
  • πŸ’‘ Rich examples for every feature
  • πŸ› Active issue tracking
  • πŸ’¬ Community discussions

πŸ”— Resources


πŸ“‹ Requirements

  • Go 1.24+ (required for generics and standard iterator support)

🀝 Contributing

We welcome contributions! Whether you're:

  • πŸ› Reporting bugs - Help us improve
  • πŸ’‘ Suggesting features - Share your ideas
  • πŸ“ Improving docs - Make documentation better
  • πŸ”§ Submitting PRs - Contribute code improvements

Every contribution makes gust better!

Development Setup
# Clone the repository
git clone https://github.com/andeya/gust.git
cd gust

# Run tests
go test ./...

# Run tests with coverage
go test -coverprofile=coverage.out ./...
go tool cover -html=coverage.out

πŸ“„ License

This project is licensed under the MIT License - see the LICENSE file for details.


Made with ❀️ for the Go community

Inspired by Rust's Result, Option, and Iterator traits

⭐ Star us on GitHub β€’ πŸ“– Documentation β€’ πŸ› Report Bug β€’ πŸ’‘ Request Feature

Directories ΒΆ

Path Synopsis
Package bitset provides a thread-safe, efficient bit set implementation with comprehensive bit manipulation operations.
Package bitset provides a thread-safe, efficient bit set implementation with comprehensive bit manipulation operations.
Package constraints provides type constraints for generic programming.
Package constraints provides type constraints for generic programming.
Package conv provides generic functions for type conversion and value transformation.
Package conv provides generic functions for type conversion and value transformation.
Package dict provides generic functions for working with maps.
Package dict provides generic functions for working with maps.
Package digit provides generic functions for numeric digit operations.
Package digit provides generic functions for numeric digit operations.
Package encrypt provides a comprehensive collection of cryptographic functions with Rust-inspired error handling.
Package encrypt provides a comprehensive collection of cryptographic functions with Rust-inspired error handling.
Package errutil provides utilities for error handling and manipulation.
Package errutil provides utilities for error handling and manipulation.
internal
Package iterator provides a complete implementation of Rust's Iterator trait in Go.
Package iterator provides a complete implementation of Rust's Iterator trait in Go.
Package option provides helper functions for working with Option types.
Package option provides helper functions for working with Option types.
Package pair provides a generic Pair type for representing pairs of values.
Package pair provides a generic Pair type for representing pairs of values.
Package random provides secure random string generation with optional timestamp encoding.
Package random provides secure random string generation with optional timestamp encoding.
Package result provides helper functions for working with Result types.
Package result provides helper functions for working with Result types.
Package syncutil provides concurrent utilities for safe concurrent programming.
Package syncutil provides concurrent utilities for safe concurrent programming.
Package vec provides generic functions for working with slices and arrays.
Package vec provides generic functions for working with slices and arrays.
Package void provides a Void type for representing the absence of a value.
Package void provides a Void type for representing the absence of a value.

Jump to

Keyboard shortcuts

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