RustlikeValues.Option 2.0.0

There is a newer version of this package available.
See the version list below for details.
dotnet add package RustlikeValues.Option --version 2.0.0
                    
NuGet\Install-Package RustlikeValues.Option -Version 2.0.0
                    
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.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="RustlikeValues.Option" Version="2.0.0" />
                    
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.0
                    
#r "nuget: RustlikeValues.Option, 2.0.0"
                    
#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.0
                    
#: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.0
                    
Install as a Cake Addin
#tool nuget:?package=RustlikeValues.Option&version=2.0.0
                    
Install as a Cake Tool

RustLike Option<T> for C#

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

Installation

dotnet add package RustLikeValues.Option

Overview

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

Key Features

  • Null Safety: Explicit handling of missing values
  • Functional API: Map, Filter, AndThen, and more
  • LINQ Support: Full integration with LINQ query syntax
  • Pattern Matching: Works with C# pattern matching
  • JSON Serialization: Built-in System.Text.Json support
  • Zero Overhead: Struct-based for optimal performance
  • IEnumerable: Treat as a collection of 0 or 1 elements

Quick Start

using RustLikeValues.RustLikeOption;

// Creating Options
var some = Option.Some(42);
var none = Option.None;  // Global None instance
var empty = Option.Empty<int>();  // Typed None

// Null safety
string? nullable = GetNullableString();
Option<string> safe = nullable != null ? Option.Some(nullable) : Option.None;

// 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 Methods

Creation

  • Option.Some<T>(value) - Creates an Option containing a value
  • Option.None - Global None instance for any type
  • Option.Empty<T>() - Creates a typed None
  • Option.TryCreate<T>(Func<T>) - Creates from a function, None on exception

Checking State

  • IsSome - Returns true if the Option contains a value
  • IsNone - Returns true if the Option is empty

Extracting Values

  • Unwrap() - Gets the value or throws if None
  • UnwrapOr(defaultValue) - Gets the value or returns default
  • UnwrapOrElse(Func<T>) - Gets the value or computes default
  • GetValueOrDefault() - Returns value or default(T), can be null

Transforming

  • Map<U>(Func<T, U>) - Transform the contained value
  • MapAsync<U>(Func<T, Task<U>>) - Async transformation
  • Filter(Func<T, bool>) - Keep value only if predicate is true
  • AndThen<U>(Func<T, Option<U>>) - Chain operations that return Option

Pattern Matching

  • Match<U>(Func<T, U> Some, Func<U> None) - Transform to a single type
  • Match(Action<T> Some, Action None) - Execute side effects

Common Patterns

Null to Option Conversion

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

// Direct conversion
Option<string> name = username != null ? Option.Some(username) : Option.None;

// Implicit conversion
Option<int> number = 42;  // Automatically Some(42)

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");

Working with Nullable Values

public Option<User> FindUserByEmail(string email)
{
    // Database might return null if user doesn't exist
    var user = database.Users.FirstOrDefault(u => u.Email == email);
    return user != null ? Option.Some(user) : Option.None;
}

// Dictionary lookups
public Option<string> GetConfigValue(string key)
{
    // Instead of checking TryGetValue
    return configDictionary.TryGetValue(key, out var value) 
        ? Option.Some(value) 
        : Option.None;
}

// Parsing that might not produce a value
public Option<Address> ParseAddress(string input)
{
    if (string.IsNullOrWhiteSpace(input))
        return Option.None;
    
    var parts = input.Split(',');
    if (parts.Length < 3)
        return Option.None;
    
    return Option.Some(new Address 
    { 
        Street = parts[0], 
        City = parts[1], 
        ZipCode = parts[2] 
    });
}

LINQ Integration

// Query syntax
var query = from user in GetUser(id)
            from email in user.Email
            where email.Contains("@company.com")
            select email.ToUpper();

// Method syntax with multiple Options
var result = options
    .Where(opt => opt.IsSome)
    .Select(opt => opt.Unwrap())
    .FirstOrDefault();

Collection Operations

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

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

Advanced Features

Global None Instance

// Use the global None for any Option type
Option<int> noNumber = Option.None;
Option<string> noString = Option.None;

// Useful for early returns
if (condition)
    return Option.None;

Deconstruction

var (isSome, value) = option;
if (isSome)
    ProcessValue(value);

JSON Serialization

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

var none = Option.Empty<int>();
var noneJson = JsonSerializer.Serialize(none);  // Output: null

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

Async Support

public async Task<Option<Data>> LoadDataAsync(string id)
{
    var response = await httpClient.GetAsync($"/api/data/{id}");
    
    if (!response.IsSuccessStatusCode)
        return Option.None;
    
    var data = await response.Content.ReadFromJsonAsync<Data>();
    return Option.Some(data);
}

// Async chaining
var result = await GetDataAsync(id)
    .MapAsync(async data => await ProcessAsync(data))
    .AndThenAsync(async processed => await ValidateAsync(processed));

Flattening Nested Options

Option<Option<int>> nested = Option.Some(Option.Some(42));
Option<int> flattened = nested.Flatten();  // Some(42)

Best Practices

  1. Prefer Option over nullable for domain models

    public class User
    {
        public string Name { get; set; }  // Required
        public Option<string> Nickname { get; set; }  // Optional
        public Option<DateTime> LastLogin { get; set; }  // Optional
    }
    
  2. Use Filter for validation

    var validEmail = ParseEmail(input)
        .Filter(email => email.Contains("@"))
        .Filter(email => email.Length > 5);
    
  3. Chain operations instead of nested if-statements

    // Instead of:
    if (option.IsSome)
    {
        var value = option.Unwrap();
        if (IsValid(value))
        {
            var result = Transform(value);
            return result;
        }
    }
    return defaultValue;
    
    // Use:
    return option
        .Filter(IsValid)
        .Map(Transform)
        .UnwrapOr(defaultValue);
    
  4. Use Match for exhaustive handling

    option.Match(
        Some: value => database.Update(value),
        None: () => logger.LogWarning("No value to update")
    );
    

Comparison with Nullable

Feature Option<T> Nullable (T?)
Explicit intent ✅ Clear optional semantics ❌ Can mean null or missing
Functional API ✅ Rich set of operations ❌ Limited operations
Chaining ✅ Map, Filter, AndThen ❌ Requires null checks
Pattern matching ✅ Full support ✅ Basic support
Reference types ✅ Works with all types ⚠️ Different behavior
Value types ✅ Consistent API ⚠️ Nullable<T> differences

Performance

  • Struct-based implementation with minimal overhead
  • Aggressive inlining for common operations
  • No heap allocations for the Option itself
  • Suitable for high-performance scenarios

Migration Guide

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

// From nullable value types
int? nullableInt = GetNullableInt();
Option<int> optionInt = nullableInt.HasValue 
    ? Option.Some(nullableInt.Value) 
    : Option.None;

// From try-patterns
// Before:
if (dictionary.TryGetValue(key, out var value))
{
    Process(value);
}

// After:
dictionary.Get(key)  // Returns Option<T>
    .Match(
        Some: Process,
        None: () => { }
    );

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 232 6/24/2025
2.0.1 151 6/20/2025
2.0.0 208 6/19/2025
1.0.1 209 6/18/2025
1.0.0 215 6/18/2025