RustlikeValues.Option 2.0.1

There is a newer version of this package available.
See the version list below for details.
dotnet add package RustlikeValues.Option --version 2.0.1
                    
NuGet\Install-Package RustlikeValues.Option -Version 2.0.1
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="RustlikeValues.Option" Version="2.0.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="RustlikeValues.Option" Version="2.0.1" />
                    
Directory.Packages.props
<PackageReference Include="RustlikeValues.Option" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add RustlikeValues.Option --version 2.0.1
                    
#r "nuget: RustlikeValues.Option, 2.0.1"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package RustlikeValues.Option@2.0.1
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=RustlikeValues.Option&version=2.0.1
                    
Install as a Cake Addin
#tool nuget:?package=RustlikeValues.Option&version=2.0.1
                    
Install as a Cake Tool

RustLike Option<T> for C#

A complete implementation of Rust's Option type for C#, providing explicit null-safety with a rich functional API.

Installation

dotnet add package RustLikeValues.Option

Overview

Option<T> represents an optional value: every Option is either Some and contains a value, or None, and does not. This provides a null-safe alternative to nullable types with powerful functional programming capabilities.

Key Features

  • Explicit Null Handling: No more NullReferenceException
  • Implicit Conversions: Natural syntax with automatic wrapping
  • Functional API: Map, Filter, AndThen for composable operations
  • LINQ Support: Full integration with C# query syntax
  • Pattern Matching: Works seamlessly with C# pattern matching
  • Zero Overhead: Struct-based implementation
  • JSON Support: Built-in System.Text.Json serialization

Quick Start

using RustLikeValues.RustLikeOption;

// Creating Options
Option<int> some = 42;                    // Implicit conversion
Option<string> none = Option.None;        // Global None instance
Option<User> maybe = Option.Some(user);   // Explicit Some

// Pattern matching
var message = option.Match(
    Some: value => $"Found: {value}",
    None: () => "Not found"
);

// Chaining operations
var result = GetUser(id)
    .Map(user => user.Email)
    .Filter(email => email.Contains("@"))
    .UnwrapOr("no-email@default.com");

Core API

Creation

// Implicit conversion from value
Option<int> opt1 = 42;

// Explicit Some
Option<string> opt2 = Option.Some("hello");

// None variants
Option<int> opt3 = Option.None;           // Global None
Option<int> opt4 = Option.Empty<int>();   // Typed None
Option<int> opt5 = default;               // Default is None

// From nullable
string? nullable = GetNullableString();
Option<string> opt6 = nullable?.ToOption() ?? Option.None;

Checking State

if (option.IsSome)
    Console.WriteLine("Has value");

if (option.IsNone)
    Console.WriteLine("No value");

// Implicit bool conversion
if (option)  // true if Some
    Process(option.Unwrap());

Extracting Values

// Unwrap - throws if None
var value = option.Unwrap();

// Safe unwrapping
var value1 = option.UnwrapOr(defaultValue);
var value2 = option.UnwrapOrElse(() => ComputeDefault());
var value3 = option.GetValueOrDefault();  // Returns default(T)

Transforming

// Map - transform the value if Some
Option<string> email = user.Map(u => u.Email);

// Filter - keep only if predicate is true
Option<int> positive = number.Filter(n => n > 0);

// AndThen - chain operations that return Option
Option<Order> order = user
    .AndThen(u => GetLatestOrder(u.Id))
    .AndThen(o => ValidateOrder(o));

Pattern Matching

// Match with return value
string result = option.Match(
    Some: value => $"Value is {value}",
    None: () => "No value"
);

// Match with side effects
option.Match(
    Some: value => Console.WriteLine(value),
    None: () => Console.WriteLine("Empty")
);

// Deconstruction
var (isSome, value) = option;
if (isSome)
    Process(value);

Common Patterns

Null to Option Conversion

public Option<User> FindUser(int id)
{
    var user = database.GetUser(id);  // Returns User?
    return user?.ToOption() ?? Option.None;
}

// Extension method for nullable types
User? nullable = GetNullableUser();
Option<User> option = nullable.ToOption();

Chaining Optional Operations

var email = GetUser(id)
    .Map(user => user.Profile)
    .Map(profile => profile.ContactInfo)
    .Map(contact => contact.Email)
    .Filter(email => IsValidEmail(email))
    .UnwrapOr("no-email@example.com");

Early Returns

public Option<ProcessResult> ProcessUser(int userId)
{
    var user = GetUser(userId);
    if (user.IsNone)
        return Option.None;  // Early return
    
    var validated = ValidateUser(user.Unwrap());
    if (validated.IsNone)
        return Option.None;
    
    return Option.Some(Process(validated.Unwrap()));
}

LINQ Integration

// Query syntax
var email = from user in GetUser(id)
            from profile in user.Profile
            where profile.IsActive
            select profile.Email;

// Method chaining with LINQ
var activeEmails = users
    .Select(u => u.Email)
    .Where(opt => opt.IsSome)
    .Select(opt => opt.Unwrap());

As IEnumerable

// Option implements IEnumerable<T>
foreach (var value in option)
{
    Console.WriteLine(value);  // Executes 0 or 1 times
}

// Use with LINQ
var doubled = option.Select(x => x * 2).FirstOrDefault();

Advanced Usage

Async Operations

public async Task<Option<Data>> LoadDataAsync(string id)
{
    if (string.IsNullOrEmpty(id))
        return Option.None;
    
    var data = await api.GetDataAsync(id);
    return data != null ? Option.Some(data) : Option.None;
}

// Async chaining
var result = await GetUserAsync(id)
    .MapAsync(async user => await enrichUserDataAsync(user))
    .AndThenAsync(async user => await ValidateAsync(user));

JSON Serialization

var some = Option.Some(42);
var json = JsonSerializer.Serialize(some);     // "42"

var none = Option<int>.None;
var json2 = JsonSerializer.Serialize(none);    // "null"

// Deserialization
var opt1 = JsonSerializer.Deserialize<Option<int>>("42");    // Some(42)
var opt2 = JsonSerializer.Deserialize<Option<int>>("null");  // None

Try Pattern

// Safe factory function
var option = Option.TryCreate(() => {
    // Code that might throw
    return riskyOperation();
});  // Returns None if exception occurs

Zip Operations

var name = Option.Some("John");
var age = Option.Some(30);

var person = name.Zip(age);  // Option<(string, int)>
// Result: Some(("John", 30))

// If either is None, result is None
var missing = name.Zip(Option<int>.None);  // None

Best Practices

  1. Use Option for optional values in domain models

    public class User
    {
        public string Name { get; set; }           // Required
        public Option<string> Nickname { get; set; } // Optional
        public Option<DateTime> BirthDate { get; set; } // Optional
    }
    
  2. Prefer chaining over nested if-statements

    // ❌ Avoid
    if (opt1.IsSome)
    {
        var value1 = opt1.Unwrap();
        var opt2 = Process(value1);
        if (opt2.IsSome)
        {
            var value2 = opt2.Unwrap();
            return Transform(value2);
        }
    }
    return defaultValue;
    
    // ✅ Prefer
    return opt1
        .AndThen(Process)
        .Map(Transform)
        .UnwrapOr(defaultValue);
    
  3. Use Filter for validation

    var validEmail = ParseEmail(input)
        .Filter(email => email.Contains("@"))
        .Filter(email => email.Length > 5);
    
  4. Return Option from methods that might not have a result

    public Option<Product> FindProduct(string sku)
    {
        // Instead of returning null or throwing
        return products.FirstOrDefault(p => p.Sku == sku)?.ToOption() 
               ?? Option.None;
    }
    

Comparison with Nullable

Feature Option<T> Nullable (T?)
Explicit intent ✅ Clear optional semantics ❌ Can mean null or uninitialized
Method chaining ✅ Map, Filter, AndThen ❌ Limited to ?. and ??
Pattern matching ✅ Full support with Match ⚠️ Basic null checks
LINQ integration ✅ IEnumerable implementation ❌ Not enumerable
Implicit conversion ✅ From T to Option<T> ⚠️ Only for value types

Performance

  • Struct-based implementation (no heap allocation)
  • Aggressive inlining for common operations
  • Zero-cost abstraction for most scenarios
  • Suitable for high-performance code

Common Method Reference

Method Description Example
Some(value) Create Some variant Option.Some(42)
None Get None variant Option.None
Map(func) Transform value if Some opt.Map(x => x * 2)
Filter(predicate) Keep if predicate true opt.Filter(x => x > 0)
AndThen(func) Chain Option-returning operations opt.AndThen(GetRelated)
Or(other) Return this or other if None opt.Or(fallback)
Match(some, none) Pattern match on state opt.Match(v => v, () => 0)
Unwrap() Extract value (throws if None) opt.Unwrap()
UnwrapOr(default) Extract value or default opt.UnwrapOr(0)

License

MIT License - see LICENSE file for details

Product Compatible and additional computed target framework versions.
.NET net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net9.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
2.0.2 141 6/24/2025
2.0.1 78 6/20/2025
2.0.0 145 6/19/2025
1.0.1 145 6/18/2025
1.0.0 145 6/18/2025