RustLikeValues 2.0.1

The owner has unlisted this package. This could mean that the package is deprecated, has security vulnerabilities or shouldn't be used anymore.
dotnet add package RustLikeValues --version 2.0.1
                    
NuGet\Install-Package RustLikeValues -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" Version="2.0.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="RustLikeValues" Version="2.0.1" />
                    
Directory.Packages.props
<PackageReference Include="RustLikeValues" />
                    
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 --version 2.0.1
                    
#r "nuget: RustLikeValues, 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@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&version=2.0.1
                    
Install as a Cake Addin
#tool nuget:?package=RustLikeValues&version=2.0.1
                    
Install as a Cake Tool

RustLikeValues

A comprehensive C# library that brings Rust's powerful Option<T> and Result<T, E> types to .NET, along with functional extensions and logging capabilities. Write safer, more expressive code by making nulls and errors explicit in your type system.

Overview

RustLikeValues provides a complete implementation of Rust's error handling patterns for C#, enabling you to:

  • Eliminate null reference exceptions with Option<T>
  • Replace exceptions with explicit error handling using Result<T, E>
  • Chain operations with railway-oriented programming
  • Log functionally with result-based logging
  • Parse and transform data safely

Packages

The library is split into focused NuGet packages:

Package Description Install
RustLikeValues.Option Option type for null-safety dotnet add package RustLikeValues.Option
RustLikeValues.Result Result type for error handling dotnet add package RustLikeValues.Result
RustLikeValues.Extensions Extensions & RLogger dotnet add package RustLikeValues.Extensions

Quick Start

Option - Handling Optional Values

using RustLikeValues.RustLikeOption;

// No more null checks!
Option<User> user = GetUser(id);  // Returns Option<User> instead of User?

var email = user
    .Map(u => u.Email)
    .Filter(e => e.Contains("@"))
    .UnwrapOr("no-email@default.com");

// Pattern matching
var message = user.Match(
    Some: u => $"Welcome, {u.Name}!",
    None: () => "User not found"
);

// Implicit conversion from values
Option<int> some = 42;        // Automatically Some(42)
Option<int> none = Option.None;

Result - Explicit Error Handling

using RustLikeValues.RustLikeResult;

// Errors are values, not exceptions
Result<int, string> Divide(int a, int b)
{
    if (b == 0)
        return "Cannot divide by zero";  // Implicit Err
    return a / b;  // Implicit Ok
}

// Chain operations with automatic error propagation
var result = Divide(10, 2)
    .AndThen(x => Divide(x, 2))
    .Map(x => x * 10)
    .MapErr(err => $"Calculation failed: {err}");

// Pattern matching
result.Match(
    Ok: value => Console.WriteLine($"Result: {value}"),
    Err: error => Console.WriteLine($"Error: {error}")
);

Extensions & Logging

using RustLikeValues.RustLikeExtension;
using RustLikeValues.Logging;

// Safe parsing
Option<int> number = "42".ParseInt();
Option<DateTime> date = "2024-01-01".ParseDateTime();

// Collection operations
var user = users.FindFirst(u => u.IsAdmin);
var value = dictionary.Get("key");  // Returns Option<T>

// Functional try-catch
var result = Try.Execute(() => JsonSerializer.Deserialize<Data>(json));

// Result-based logging
var logger = new RLogger<MyClass>();
logger.LogInfo($"Processing user {userId}");

// Log with Option/Result integration
var data = GetData(id);
if (logger.IfNone(data, "Data not found"))
    return;  // Early exit if None

Why RustLikeValues?

1. Null Safety with Option

// ❌ Traditional C# - NullReferenceException waiting to happen
public User? GetUser(int id) { ... }
var user = GetUser(1);
Console.WriteLine(user.Name);  // 💥 Possible NullReferenceException

// ✅ With Option - Null safety enforced by type system
public Option<User> GetUser(int id) { ... }
var user = GetUser(1);
user.Match(
    Some: u => Console.WriteLine(u.Name),  // Only executes if user exists
    None: () => Console.WriteLine("No user found")
);

2. Explicit Error Handling with Result

// ❌ Traditional C# - Exceptions are hidden
public Order ProcessOrder(OrderRequest request)
{
    ValidateRequest(request);  // Might throw ValidationException
    var order = CreateOrder(request);  // Might throw OutOfStockException
    ChargePayment(order);  // Might throw PaymentException
    return order;
}

// ✅ With Result - All errors are visible in the signature
public Result<Order, OrderError> ProcessOrder(OrderRequest request)
{
    return ValidateRequest(request)
        .AndThen(CreateOrder)
        .AndThen(ChargePayment);  // Errors propagate automatically
}

3. Composable Operations

// Chain operations without nested if-statements or try-catch blocks
var finalResult = GetUser(userId)
    .Map(user => user.Profile)
    .Filter(profile => profile.IsActive)
    .AndThen(profile => UpdateLastLogin(profile))
    .MapErr(error => new ServiceError($"Failed to update user: {error}"))
    .Tap(profile => logger.LogInfo($"Updated profile: {profile.Id}"))
    .TapErr(error => logger.LogError(error.Message));

4. Smart Implicit Conversions

// Implicit conversions make the API natural to use
Result<User, string> GetUser(int id)
{
    if (id < 0)
        return "Invalid user ID";  // Automatically Err
    
    var user = database.Find(id);
    if (user == null)
        return "User not found";   // Automatically Err
        
    return user;  // Automatically Ok
}

// Option also supports implicit conversion
Option<string> name = "John";  // Automatically Some("John")
Option<string> empty = Option.None;

Real-World Example

Here's a complete example showing how these types work together in a real application:

public class UserService
{
    private readonly IUserRepository _repository;
    private readonly RLogger<UserService> _logger;
    
    public async Task<Result<UserDto, ApiError>> GetUserAsync(string email)
    {
        _logger.LogInfo($"Fetching user by email: {email}");
        
        // Parse and validate email
        var validEmail = email
            .ToOption()
            .Filter(e => e.Contains("@"))
            .ToResult(new ApiError("Invalid email format"));
            
        if (_logger.IfErr(validEmail, "Email validation failed"))
            return validEmail.Map(e => new UserDto());  // Type conversion
        
        // Fetch from database
        var user = await validEmail
            .AndThenAsync(async e => await _repository.FindByEmailAsync(e))
            .AndThen(opt => opt.ToResult(new ApiError("User not found")));
            
        // Transform and return
        return user
            .Map(u => new UserDto 
            { 
                Id = u.Id,
                Email = u.Email,
                Name = u.Name 
            })
            .Tap(dto => _logger.LogInfo($"Successfully fetched user {dto.Id}"))
            .TapErr(err => _logger.LogWarning($"Failed to fetch user: {err}"));
    }
    
    public Result<List<int>, ApiError> ParseUserIds(string input)
    {
        return Try.Execute(() =>
        {
            return input
                .Split(',')
                .Select(s => s.Trim())
                .FilterMap(s => s.ParseInt())  // Safe parsing
                .Where(id => id > 0)
                .ToList();
        })
        .MapErr(ex => new ApiError($"Invalid ID format: {ex.Message}"));
    }
}

Key Benefits

  1. Type Safety: Null and error cases are explicit in method signatures
  2. Composability: Chain operations without nesting or early returns
  3. Performance: Zero-cost abstractions with struct implementations
  4. Expressiveness: Code clearly shows intent and possible outcomes
  5. Reduced Bugs: Impossible to forget null checks or error handling
  6. Better APIs: Method signatures tell the whole story

License

This project is licensed under the MIT License - see the LICENSE file for details.


Make your C# code safer and more expressive with RustLikeValues!

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