Skip to content
/ gust Public

A production-ready library that makes error handling, optional values, and iteration as beautiful and safe as in Rust.

License

Notifications You must be signed in to change notification settings

andeya/gust

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

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 organized into three categories:

🎯 Core Packages

The foundation of gust's type-safe programming model:

gust/result - Type-Safe Error Handling

Eliminate if err != nil boilerplate with the revolutionary Catch pattern:

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

func readFile(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 Features: Result[T], Catch pattern, Map, AndThen, UnwrapOr

gust/option - Safe Optional Values

Replace *T and (T, bool) with type-safe Option[T]:

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

divide := func(a, b float64) option.Option[float64] {
    if b == 0 {
        return option.None[float64]()
    }
    return option.Some(a / b)
}

quotient := divide(10, 2).UnwrapOr(0)  // Safe: never panics

Key Features: Option[T], Map, Filter, AndThen, UnwrapOr

gust/iterator - Rust-like Iteration

60+ methods for declarative data processing:

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

sum := iterator.FromSlice([]int{1, 2, 3, 4, 5}).
    Filter(func(x int) bool { return x%2 == 0 }).
    Map(func(x int) int { return x * x }).
    Fold(0, func(acc, x int) int { return acc + x })

Key Features: 60+ methods, lazy evaluation, method chaining, Go 1.24+ integration

πŸ“Š Data Structure Packages

Generic utilities for common data structures:

gust/dict - Generic Map Utilities

Type-safe map operations with Option return types:

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

m := map[string]int{"a": 1, "b": 2}
value := dict.Get(m, "b")  // Returns Option[int]
if value.IsSome() {
    fmt.Println(value.Unwrap())  // 2
}

Key Features: Filter, Map, Keys, Values, Get

gust/vec - Generic Slice Utilities

Type-safe slice operations:

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

numbers := []int{1, 2, 3}
doubled := vec.MapAlone(numbers, func(x int) int { return x * 2 })

Key Features: MapAlone, Get, Copy, Dict

gust/bitset - Thread-Safe Bit Sets

Efficient bit manipulation with iterator integration:

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

bs := bitset.New(64)
bs.Set(0).Set(2).Set(5)
ones := iterator.FromBitSetOnes(bs).Collect()  // [0, 2, 5]

Key Features: Bitwise operations, iterator integration, multiple encodings

πŸ› οΈ Utility Packages

Production-ready tools for common tasks:

gust/conv - Type-Safe Conversions

Zero-copy conversions and string manipulation:

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

// Zero-copy byte/string conversion
bytes := []byte{'h', 'e', 'l', 'l', 'o'}
str := conv.BytesToString[string](bytes)

// Case conversion
snake := conv.ToSnakeCase("UserID")      // "user_id"
camel := conv.ToCamelCase("user_id")     // "UserId"

Key Features: BytesToString, StringToReadonlyBytes, case conversion, JSON quoting

gust/digit - Number Conversions

Base 2-62 conversion with overflow protection:

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

// Format in different bases
fmt.Println(digit.FormatUint(255, 16))  // "ff"
fmt.Println(digit.FormatUint(255, 62))  // "47"

// Parse with error handling
parsed := digit.ParseInt("ff", 16, 64)  // Result[int64]

Key Features: Base 2-62 conversion, FormatInt, ParseInt, checked operations

gust/random - Secure Random Strings

Cryptographically secure random generation with timestamp embedding:

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

gen := random.NewGenerator(false)  // base62 encoding
str := gen.RandomString(16).Unwrap()
timestamped := gen.StringWithNow(20).Unwrap()  // Includes timestamp

Key Features: Base36/Base62 encoding, timestamp embedding, secure generation

gust/encrypt - Cryptographic Functions

Complete hash suite and AES encryption:

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

// Hash functions
hash := encrypt.SHA256([]byte("hello"), encrypt.EncodingHex)

// AES encryption
key := []byte("1234567890123456")
encrypted := encrypt.EncryptAES(key, []byte("secret"), 
    encrypt.ModeCBC, encrypt.EncodingHex)

Key Features: MD5, SHA series, FNV, CRC, Adler-32, AES encryption

gust/errutil - Error Utilities

Enhanced error handling with stack traces:

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

// Error boxing
errBox := errutil.BoxErr(errors.New("error"))
err := errBox.ToError()

// Stack traces
stack := errutil.GetStackTrace(0)

Key Features: Stack traces, panic recovery, ErrBox

gust/fileutil - File Operations

Type-safe file operations with Result types:

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

// Copy file
res := fileutil.CopyFile("src.txt", "dst.txt")

// Archive directory
fileutil.TarGz("src/", "archive.tar.gz", false, nil, ".git")

Key Features: Path manipulation, file I/O, directory operations, tar.gz archiving

gust/coarsetime - Fast Coarse-Grained Time

30x faster than time.Now() for high-frequency operations:

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

// Use default instance (100ms precision)
now := coarsetime.Now()
elapsed := coarsetime.Since(start)  // Monotonic time

// Create instance with default precision (100ms)
ct := coarsetime.New()  // Uses default 100ms precision
defer ct.Stop()
now := ct.Now()

// Create custom precision instance
ct2 := coarsetime.New(10 * time.Millisecond)  // 10ms precision
defer ct2.Stop()
now2 := ct2.Now()

Key Features: Wall clock & monotonic time, configurable precision, 30x faster

gust/shutdown - Graceful Shutdown & Reboot

Signal handling and cleanup hooks:

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

s := shutdown.New()
s.SetHooks(
    func() result.VoidResult { /* first sweep */ },
    func() result.VoidResult { /* final cleanup */ },
)
go s.Listen()  // Blocks until signal

Key Features: Signal handling, cleanup hooks, graceful process restart (Unix)

gust/syncutil - Concurrent Utilities

Thread-safe data structures:

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

// Thread-safe map
var m syncutil.SyncMap[string, int]
m.Store("key", 42)
value := m.Load("key")  // Returns Option[int]

// Lazy initialization
lazy := syncutil.NewLazyValueWithFunc(func() result.Result[int] {
    return result.Ok(expensiveComputation())
})

Key Features: SyncMap, Lazy, mutex wrappers

gust/constraints - Type Constraints

Generic type constraints for compile-time safety:

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

func max[T constraints.Ord](a, b T) T {
    if constraints.Compare(a, b).IsGreater() {
        return a
    }
    return b
}

Key Features: Ordering, Numeric, Digit


🎯 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

About

A production-ready library that makes error handling, optional values, and iteration as beautiful and safe as in Rust.

Topics

Resources

License

Stars

Watchers

Forks

Languages