Usage

The 18 examples below are taken from zig-error-handling's README.

.{
    .name = "my-project",
    .version = "0.1.0",
    .dependencies = .{
        .result = .{
            .url = "https://github.com/yourusername/zig-result/archive/refs/tags/v0.1.0.tar.gz",
            // Add hash after first fetch
        },
    },
}
const result = b.dependency("result", .{
    .target = target,
    .optimize = optimize,
});

exe.root_module.addImport("result", result.module("result"));
const std = @import("std");
const Result = @import("result").Result;

fn divide(a: i32, b: i32) Result(i32, []const u8) {
    if (b == 0) {
        return Result(i32, []const u8).err("Division by zero");
    }
    return Result(i32, []const u8).ok(@divTrunc(a, b));
}

pub fn main() !void {
    const result = divide(10, 2);

    if (result.isOk()) {
        std.debug.print("Result: {d}\n", .{result.unwrap()});
    } else {
        std.debug.print("Error: {s}\n", .{result.unwrapErr()});
    }
}
// Create an Ok result
const success = Result(i32, []const u8).ok(42);

// Create an Err result
const failure = Result(i32, []const u8).err("Something went wrong");

// Check result status
if (success.isOk()) { /_ ... _/ }
if (failure.isErr()) { /_ ... _/ }
const result = Result(i32, []const u8).ok(21);

const doubled = result.map(i32, struct {
    fn double(x: i32) i32 {
        return x * 2;
    }
}.double);

// doubled is Result(i32, []const u8).ok(42)
const result = Result(i32, []const u8).err("failed");

const mapped = result.mapErr([]const u8, struct {
    fn toUpper(s: []const u8) []const u8 {
        return "FAILED";
    }
}.toUpper);

// mapped is Result(i32, []const u8).err("FAILED")
fn parseNumber(str: []const u8) Result(i32, []const u8) {
    const num = std.fmt.parseInt(i32, str, 10) catch {
        return Result(i32, []const u8).err("Invalid number");
    };
    return Result(i32, []const u8).ok(num);
}

fn validatePositive(n: i32) Result(i32, []const u8) {
    if (n <= 0) {
        return Result(i32, []const u8).err("Number must be positive");
    }
    return Result(i32, []const u8).ok(n);
}

const result = parseNumber("42").andThen(i32, validatePositive);
// If parsing succeeds, validates the number
// If either fails, returns the error
const result = Result(i32, []const u8).err("failed");

const recovered = result.orElse(struct {
    fn fallback(e: []const u8) Result(i32, []const u8) {
        _ = e;
        return Result(i32, []const u8).ok(0); // Default value
    }
}.fallback);

// recovered is Result(i32, []const u8).ok(0)
const result = Result(i32, []const u8).ok(42);

const message = result.match([]const u8, .{
    .ok = struct {
        fn handleOk(x: i32) []const u8 {
            return "Success!";
        }
    }.handleOk,
    .err = struct {
        fn handleErr(e: []const u8) []const u8 {
            return e;
        }
    }.handleErr,
});
// Get value or panic
const value = result.unwrap();

// Get value or use default
const value = result.unwrapOr(0);

// Get value or compute from error
const value = result.unwrapOrElse(struct {
    fn compute(e: []const u8) i32 {
        return 0;
    }
}.compute);

// Get value or panic with custom message
const value = result.expect("Expected a valid number");
const r1 = Result(i32, []const u8).ok(10);
const r2 = Result(i32, []const u8).ok(20);

const combined = r1.combine(r2);
// combined is Result(struct { i32, i32 }, []const u8).ok(.{ 10, 20 })

const values = combined.unwrap();
const sum = values[0] + values[1]; // 30
const fromErrorUnion = @import("result").fromErrorUnion;

// Convert error union to Result
const errorUnion: anyerror!i32 = 42;
const result = fromErrorUnion(errorUnion);
// result is Result(i32, anyerror).ok(42)

// Convert Result to error union for safe unwrapping
const result = Result(i32, anyerror).ok(42);
const error_union = result.toErrorUnion(); // Returns anyerror!i32
const results = [_]Result(i32, []const u8){
    Result(i32, []const u8).ok(1),
    Result(i32, []const u8).ok(2),
    Result(i32, []const u8).ok(3),
};

const collected = collect(i32, []const u8, allocator, &results);
// If all Ok: Result([]i32, []const u8).ok([1, 2, 3])
// If any Err: Returns first error
defer if (collected.isOk()) allocator.free(collected.unwrap());
const results = [_]Result(i32, []const u8){
    Result(i32, []const u8).ok(1),
    Result(i32, []const u8).err("error1"),
    Result(i32, []const u8).ok(2),
};

const partitioned = try partition(i32, []const u8, allocator, &results);
defer allocator.free(partitioned.oks);
defer allocator.free(partitioned.errs);
// partitioned.oks = [1, 2]
// partitioned.errs = ["error1"]
const results = [_]Result(i32, []const u8){
    Result(i32, []const u8).ok(10),
    Result(i32, []const u8).ok(20),
};

const sequenced = sequence(i32, []const u8, allocator, &results);
// Stops at first error, otherwise returns all Ok values
defer if (sequenced.isOk()) allocator.free(sequenced.unwrap());
const result = Result(i32, []const u8).ok(42)
    .inspect(struct {
        fn log(val: i32) void {
            std.debug.print("Value: {d}\n", .{val});
        }
    }.log)
    .inspectErr(struct {
        fn logErr(e: []const u8) void {
            std.debug.print("Error: {s}\n", .{e});
        }
    }.logErr);
const transpose = @import("result").transpose;

const result_opt = Result(?i32, []const u8).ok(42);
const opt_result = transpose(i32, []const u8, result_opt);
// Some(Result(i32).ok(42))

const result_none = Result(?i32, []const u8).ok(null);
const none = transpose(i32, []const u8, result_none);
// null
const nested = Result(Result(i32, []const u8), []const u8).ok(
    Result(i32, []const u8).ok(42)
);
const flattened = nested.flatten();
// Result(i32, []const u8).ok(42)