erratic 0.8.3

Handling errors in an efficient way.
Documentation

Erratic /ɪˈrætɪk/

license crates.io docs.rs

This library provides Error<S = Stateless>, an optionally dynamic dispatched error type, enabling applications to handle errors uniformly across different contexts.

Quick Start

In most cases, Error can serve as a drop-in replacement for Box<dyn Error>. Compared to the latter, it occupies only 1 usize, making the happy path faster.

use erratic::*;

fn write_log(filename: String) -> Result<()> {
    File::open(&filename)?.write_all(b"Hello, World!")?;
    Ok(())
}

Attaching Context & Payload

When constructing an error, you can optionally attach a static context and/or a dynamic payload. If attached, their memory is merged into a single allocation when the upstream error is erased. If omitted, no extra memory is allocated for them. If only a context is provided, no heap allocation occurs at all.

use erratic::*;

fn write_log(filename: String) -> Result<()> {
    File::open(&filename)
        .ok()
        .with_context(literal!("failed to open the log file"))? // No alloc.
        .write_all(b"Hello, World!")
        .with_context(literal!("while writing to"))
        .with_payload(filename)?; // Alloc once for `io::Error`, `filename`, and `Context`.
    Ok(())
}

Binding State

When propagating an error that requires special handling, you can attach a generic state to it. The state is optional and can be cheaply erased or extracted using extract_state.

When the state is small enough and none of the source error, context, or payload is attached, the state is inlined without any heap allocation.

use erratic::*;

#[derive(Debug)]
enum WriteLog {
    FileNotFound,
}

fn write_log(filename: String) -> std::result::Result<(), Error<WriteLog>> {
    File::open(&filename)
        .ok()
        .with_state(WriteLog::FileNotFound)? // No alloc.
        .write_all(b"Hello, World!")
        .with_context(literal!("while writing to"))
        .with_payload(filename)?;
    Ok(())
}

Representation

Type-wise, Error<S> is an internally tagged union, and it requires pointers to constant or heap-allocated data to be aligned to 4 bytes, freeing up the lower 2 bits to encode the discriminant. This design allows heap allocation to be avoided when unnecessary.

(32-bit platform, little-endian)
(Context Only)
[XXXXXX00|XXXXXXXX|XXXXXXXX|XXXXXXXX]
                                    \
                                    `rodata-> [Context]
(State Only)
[00000010|     ~    State     ~     ]

(Otherwise)
[XXXXXX01|XXXXXXXX|XXXXXXXX|XXXXXXXX]
                                    \
                                    `heap-> [VTable|State|Error|Payload|Context]

Contributing

Contributions are warmly welcomed! Whether you have a bug report, feature request, or an improvement in mind, feel free to open an issue or submit a pull request. All ideas—big or small—help make this library better for everyone.