Skip to main content

Crate erratic

Crate erratic 

Source
Expand description

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]

Modules§

context
Context traits and its placeholder for Builder.
nae
Error placeholder for Builder.
payload
Payload placeholder for Builder.
state
State traits and the Stateless marker.

Macros§

literal
Creates a literal value, or declares one or more named literal types.
match_else
Like let-else, but also handles the remaining cases — for Result only.
mkerr
Constructs an Error from a variety of input types.
mkres
Shorthand for Err(mkerr!(..)).

Structs§

Builder
An intermediate builder for constructing an Error.
Error
An error type that can carry optional state, source, context, and payload.

Traits§

BuilderExt
Extension trait for attaching context, state, or payload to an existing error.
ErrorExt
Extension trait for materializing or erasing an error.
StateExt
Extension trait for extracting the state to a separate layer.

Type Aliases§

Result