π― 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.
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.
- π 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.