{"id":30582921,"url":"https://github.com/baryon-asymm/zeroresult","last_synced_at":"2026-04-06T01:31:39.737Z","repository":{"id":298781166,"uuid":"998254556","full_name":"baryon-asymm/ZeroResult","owner":"baryon-asymm","description":"ZeroResult provides allocation-free result monads for .NET 8+ with full async support and fluent APIs. Perfect for high-performance applications where traditional exception handling is too costly.","archived":false,"fork":false,"pushed_at":"2025-06-29T22:22:57.000Z","size":143,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-10-05T01:22:41.133Z","etag":null,"topics":["csharp","error-handling","result-monad","zero-allocation"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/baryon-asymm.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-06-08T07:52:02.000Z","updated_at":"2025-06-29T22:20:21.000Z","dependencies_parsed_at":"2025-08-29T08:38:06.216Z","dependency_job_id":null,"html_url":"https://github.com/baryon-asymm/ZeroResult","commit_stats":null,"previous_names":["baryon-asymm/zeroresult"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/baryon-asymm/ZeroResult","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/baryon-asymm%2FZeroResult","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/baryon-asymm%2FZeroResult/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/baryon-asymm%2FZeroResult/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/baryon-asymm%2FZeroResult/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/baryon-asymm","download_url":"https://codeload.github.com/baryon-asymm/ZeroResult/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/baryon-asymm%2FZeroResult/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31456602,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-05T21:22:52.476Z","status":"ssl_error","status_checked_at":"2026-04-05T21:22:51.943Z","response_time":75,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["csharp","error-handling","result-monad","zero-allocation"],"created_at":"2025-08-29T08:08:42.455Z","updated_at":"2026-04-06T01:31:39.723Z","avatar_url":"https://github.com/baryon-asymm.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ZeroResult: High-Performance Result Monads for .NET 🌟\n\n[![NuGet](https://img.shields.io/nuget/v/ZeroResult.svg)](https://www.nuget.org/packages/ZeroResult/)\n[![CI](https://github.com/baryon-asymm/ZeroResult/actions/workflows/ci.yml/badge.svg)](https://github.com/baryon-asymm/ZeroResult/actions)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](./LICENSE)\n\nZeroResult provides allocation-free result monads for .NET 8+ with full async support and fluent APIs. Perfect for high-performance applications where traditional exception handling is too costly.\n\n## Why ZeroResult? 🚀\n\n✅ **Zero Allocations in Happy Path**  \n`StackResult` uses `ref struct` to eliminate heap allocations completely when operations succeed\n\n✅ **Modern C# 12+ Integration**  \nLeverages the latest language features for optimal performance and expressiveness\n\n✅ **Seamless Async Support**  \nFull async/await compatibility with ValueTask-based operations and fluent chaining\n\n## Features ✨\n\n- Dual result types: `StackResult` (allocation-free `ref struct`) and `Result` (flexible `readonly struct`)\n- Full async/await support with ValueTask\n- Fluent API: `Map`, `Bind`, `Match`, `Ensure`, `Tap`\n- Comprehensive error handling with `IError`, `SingleError`, and `MultiError`\n- Optimized for .NET 8+ and C# 12\n- Implicit conversions for clean syntax\n- Stack-safe operations with inlining\n- Comprehensive benchmark suite\n\n## Installation 📦\n\n```bash\ndotnet add package ZeroResult\n```\n\n## Quick Examples 💻\n\n### 1. Basic Result Handling with Match\n```csharp\nResult\u003cint, SingleError\u003e Calculate(int input)\n{\n    return input != 0 \n        ? 100 / input \n        : new SingleError(\"Division by zero\");\n}\n\nvar result = Calculate(10);\nresult.Match(\n    onSuccess: value =\u003e Console.WriteLine($\"Result: {value}\"),\n    onFailure: error =\u003e Console.WriteLine($\"Error: {error.Message}\")\n);\n```\n\n### 2. Fluent API Chaining with Map and Bind\n```csharp\nResult\u003cint, SingleError\u003e result = Result.Success\u003cint, SingleError\u003e(5)\n    .Map(x =\u003e x * 2)  // Transforms value if successful\n    .Bind(x =\u003e x \u003c 100 \n        ? x * 3 \n        : Result.Failure\u003cint, SingleError\u003e(new SingleError(\"Too large\")))\n    .Ensure(x =\u003e x % 2 == 0, () =\u003e new SingleError(\"Must be even\"));\n```\n\n### 3. Async Operations with ValueTask\n```csharp\nasync ValueTask\u003cResult\u003cstring, SingleError\u003e\u003e ProcessDataAsync(int id)\n{\n    return await FetchDataAsync(id)\n        .MapAsync(async data =\u003e await TransformAsync(data))\n        .BindAsync(async transformed =\u003e await ValidateAsync(transformed))\n        .OnSuccessAsync(async result =\u003e await LogSuccessAsync(result))\n        .OnFailureAsync(async error =\u003e await LogErrorAsync(error));\n}\n```\n\n### 4. Conditional Validation with Ensure\n```csharp\nResult\u003cUser, ValidationError\u003e ValidateUser(User user)\n{\n    return Result.Success\u003cUser, ValidationError\u003e(user)\n        .Ensure(u =\u003e u.Age \u003e= 18, () =\u003e new ValidationError(\"Underage\"))\n        .Ensure(u =\u003e !string.IsNullOrEmpty(u.Email), () =\u003e new ValidationError(\"Invalid email\"));\n}\n```\n\n### 5. Combining Sync and Async Operations\n```csharp\nasync ValueTask\u003cResult\u003cReport, BusinessError\u003e\u003e GenerateReportAsync(int userId)\n{\n    return await GetUser(userId)\n        .Map(user =\u003e new ReportRequest(user))\n        .BindAsync(async request =\u003e await ValidateRequestAsync(request))\n        .MapAsync(async validRequest =\u003e await GenerateReportAsync(validRequest));\n}\n```\n\n### 6. Error Handling Comparison\n```csharp\n// Traditional approach (expensive exceptions)\ntry {\n    var value = RiskyOperation();\n    Process(value);\n}\ncatch (Exception ex) {\n    HandleError(ex);\n}\n\n// ZeroResult approach (explicit error handling)\nvar result = SafeOperation();\nresult.Match(\n    onSuccess: Process,\n    onFailure: HandleError\n);\n```\n\n### 7. Advanced Matching with Return Values\n```csharp\nResult\u003cOrder, OrderError\u003e orderResult = ProcessOrder(orderId);\nstring message = orderResult.Match(\n    onSuccess: order =\u003e $\"Order {order.Id} processed\",\n    onFailure: error =\u003e $\"Failed: {error.Message}\"\n);\n\n// Async version\nstring asyncMessage = await orderResult.MatchAsync(\n    onSuccess: async order =\u003e await FormatOrderAsync(order),\n    onFailure: async error =\u003e await FormatErrorAsync(error)\n);\n```\n\n### 8. Side Effects with Tap\n```csharp\nawait GetUserAsync(userId)\n    .TapAsync(async user =\u003e await AuditAccessAsync(user))\n    .MapAsync(user =\u003e user.Profile)\n    .Tap(profile =\u003e CacheProfile(profile));\n```\n\n## Advanced Error Handling with MultiError 🚨\n\nZeroResult's `MultiError` provides sophisticated error aggregation for complex validation scenarios, batch processing, and cases where multiple failures need to be reported simultaneously.\n\n### Key Features:\n- **Efficient error collection** with minimal allocations\n- **Builder pattern** for incremental construction\n- **Automatic message formatting** with error codes\n- **Merge capability** for combining error sets\n- **Lazy evaluation** of error messages\n\n### Usage Examples:\n\n#### 1. Basic MultiError Creation\n```csharp\nvar errors = new IError[] {\n    new SingleError(\"Invalid email format\", \"VAL-001\"),\n    new SingleError(\"Password must be 8+ characters\", \"SEC-002\"),\n    new SingleError(\"Username already exists\", \"USER-003\")\n};\n\nResult\u003cUnit, MultiError\u003e validationResult = new MultiError(errors);\n```\n\n#### 2. Builder Pattern for Validation\n```csharp\nResult\u003cUser, MultiError\u003e ValidateUser(UserInput input)\n{\n    var builder = MultiError.CreateBuilder();\n    \n    if (string.IsNullOrEmpty(input.Email))\n        builder.Add(new SingleError(\"Email required\", \"REQ-001\"));\n    \n    if (input.Password.Length \u003c 8)\n        builder.Add(new SingleError(\"Password too short\", \"SEC-001\"));\n    \n    if (input.Age \u003c 18)\n        builder.Add(new SingleError(\"Must be 18+\", \"AGE-001\"));\n    \n    return builder.Count \u003e 0\n        ? Result.Failure\u003cUser, MultiError\u003e(builder.Build())\n        : MapToUser(input);\n}\n```\n\n#### 3. Batch Processing with Error Aggregation\n```csharp\nasync ValueTask\u003cResult\u003cBatchReport, MultiError\u003e\u003e ProcessBatchAsync(int[] ids)\n{\n    var builder = MultiError.CreateBuilder();\n    var successes = new List\u003cItemResult\u003e();\n    \n    foreach (var id in ids)\n    {\n        var result = await ProcessItemAsync(id);\n        result.Match(\n            onSuccess: successes.Add,\n            onFailure: builder.Add\n        );\n    }\n    \n    return builder.Count \u003e 0\n        ? Result.Failure\u003cBatchReport, MultiError\u003e(builder.Build())\n        : new BatchReport(successes);\n}\n```\n\n#### 4. Merging Multiple Error Sets\n```csharp\nvar addressResult = ValidateAddress(order.Address);\nvar paymentResult = ValidatePayment(order.PaymentMethod);\n\nif (addressResult.IsFailure || paymentResult.IsFailure)\n{\n    var mergedErrors = MultiError.Merge(\n        addressResult.IsFailure ? addressResult.Error : MultiError.Empty,\n        paymentResult.IsFailure ? paymentResult.Error : MultiError.Empty\n    );\n    \n    return Result.Failure\u003cOrderConfirmation, MultiError\u003e(mergedErrors);\n}\n```\n\n#### 5. Complex Domain Validation\n```csharp\nResult\u003cLoanApplication, MultiError\u003e ValidateApplication(LoanApplication app)\n{\n    var builder = MultiError.CreateBuilder();\n    \n    // Financial validation\n    if (app.Income \u003c app.MonthlyPayment * 3)\n        builder.Add(new SingleError(\"Income insufficient\", \"FIN-001\"));\n    \n    // Document validation\n    if (app.RequiredDocuments.Count \u003c 3)\n        builder.Add(new SingleError(\"Missing documents\", \"DOC-002\"));\n    \n    // Business rules\n    if (app.Age \u003c 21)\n        builder.Add(new SingleError(\"Minimum age not met\", \"AGE-003\"));\n    \n    // Custom validation method\n    ValidateCreditHistory(app.CreditScore, builder);\n    \n    return builder.Count \u003e 0\n        ? Result.Failure\u003cLoanApplication, MultiError\u003e(builder.Build())\n        : app;\n}\n\nvoid ValidateCreditHistory(int score, MultiErrorBuilder builder)\n{\n    if (score \u003c 650)\n        builder.Add(new SingleError(\"Poor credit history\", \"CRD-004\"));\n}\n```\n\n#### 6. Formatted Error Output\nMultiError automatically generates structured error messages:\n```csharp\nvar error = new MultiError(new IError[] {\n    new SingleError(\"Invalid email format\", \"VAL-001\"),\n    new SingleError(\"Password too short\", \"SEC-002\"),\n    new SingleError(\"Terms not accepted\", \"REQ-003\")\n});\n\nConsole.WriteLine(error.Message);\n/* Output:\nMultiple errors occurred (3):\n- Invalid email format (Code: VAL-001)\n- Password too short (Code: SEC-002)\n- Terms not accepted (Code: REQ-003)\n*/\n```\n\n### Performance Optimizations\n- **Pre-allocated collections**: Builder uses optimal initial capacity\n- **Struct-based enumerators**: Avoids boxing allocations\n- **Lazy message formatting**: Message concatenation deferred until first access\n- **Merge without copying**: Reuses existing error collections\n\n### When to Use MultiError\n✔ Complex form validations  \n✔ Batch processing pipelines  \n✔ Distributed system integrations  \n✔ Business rule engines  \n✔ Data migration tools  \n\n---\n\n**Pro Tip**: Combine `MultiError` with `StackResult` for allocation-free validation in hot paths:\n```csharp\nStackResult\u003cTransaction, MultiError\u003e ValidateTransaction(Transaction tx)\n{\n    var builder = MultiError.CreateBuilder();\n    // ... validation logic\n    return builder.Count \u003e 0\n        ? StackResult.Failure\u003cTransaction, MultiError\u003e(builder.Build())\n        : tx;\n}\n```\n\n## Performance ⚡\n\nZeroResult dramatically outperforms traditional exception handling, especially in deep call stacks and error scenarios. Benchmarks were run on **.NET 9.0.6** using **BenchmarkDotNet v0.15.1**, with results collected from two major platforms:\n\n- **Windows x64**: AMD Ryzen 7 7800X3D\n- **macOS Arm64**: Apple M2\n\n### Key Findings:\n- **100-180x faster** than exceptions in method chaining scenarios\n- **77-89% less memory allocated** in failure cases\n- **Zero allocations** in success paths with imperative style\n- **Handles 200+ call depths** where exceptions cause stack overflows\n- **Fluent APIs add minimal overhead** (~2μs) compared to imperative style\n\n### Benchmark Highlights\n\n#### 1. Method Chaining (2000 iterations)\n| Scenario                | Approach               | Mean Time  | Allocated | vs Try/Catch |\n|-------------------------|------------------------|------------|-----------|--------------|\n| **All Failures**        | Try/Catch              | 2,521 μs   | 427 KB    | 1.00x        |\n|                         | StackResult (Imperative)| 14.8 μs    | 47 KB     | **170x faster** |\n|                         | Result (Fluent)        | 24.7 μs    | 175 KB    | **100x faster** |\n| **75% Success Rate**    | Try/Catch              | 631 μs     | 103 KB    | 1.00x        |\n|                         | StackResult (Imperative)| 18.4 μs    | 11 KB     | **34x faster** |\n|                         | Result (Fluent)        | 27.8 μs    | 139 KB    | **23x faster** |\n\n#### 2. Deep Call Stack Performance\n| Approach                | Call Depth | Mean Time  | Memory  | Outcome            |\n|-------------------------|------------|------------|---------|--------------------|\n| **Exceptions**          | 20         | 111 ms     | 15.7 MB | Works              |\n|                         | 200        | -          | -       | **Stack Overflow** |\n| **ZeroResult (MultiError)** | 20      | 2.6 ms     | 6.9 MB  | **43x faster**     |\n|                         | 200        | 25 ms      | 65 MB   | Still works        |\n\n#### 3. Memory Efficiency\n| Scenario                | Approach       | Allocations | Reduction |\n|-------------------------|----------------|-------------|-----------|\n| Async Operations (100% errors) | Try/Catch | 2.77 MB     | -         |\n|                         | ZeroResult     | 625 KB      | **77% less** |\n| Method Chaining (100% errors) | Try/Catch | 427 KB      | -         |\n|                         | ZeroResult     | 47 KB       | **89% less** |\n\n### Performance Takeaways\n\n1. **Error Handling:** ZeroResult is **100-170x faster** with **77-89% less memory** than exceptions\n2. **Success Paths:** Near-zero overhead with **sub-microsecond operations**\n3. **Scalability:** Handles call depths impossible with exceptions\n4. **Fluent APIs:** Add just **10μs overhead** vs imperative style while being more expressive\n5. **Memory Efficiency:** Dramatically reduces GC pressure in error scenarios\n\nIf you're interested in the full benchmark results or want to explore detailed metrics for specific scenarios, check out the [**benchmark results folder**](./benchmarks/results/).\n\n---\n\n**ZeroResult** - Where performance meets reliability in .NET error handling. Contribute on [GitHub](https://github.com/baryon-asymm/ZeroResult)!\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbaryon-asymm%2Fzeroresult","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbaryon-asymm%2Fzeroresult","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbaryon-asymm%2Fzeroresult/lists"}