4 stable releases

1.0.3 Feb 7, 2025
1.0.2 Nov 6, 2024
1.0.1 Nov 1, 2024
1.0.0 Oct 30, 2024

#1388 in Rust patterns

Download history 256/week @ 2024-10-30 164/week @ 2024-11-06 5/week @ 2024-11-13 3/week @ 2024-11-20 10/week @ 2024-12-04 11/week @ 2024-12-11 125/week @ 2025-02-05

125 downloads per month

Apache-2.0 OR MIT

105KB
1.5K SLoC

Lithium

License Version docs.rs Tests

Lightweight exceptions.

Lithium provides a custom exception mechanism as an alternative to Rust panics. Compared to Rust panics, this mechanism is allocation-free, avoids indirections and RTTI, and is hence faster, if less applicable.

On nightly, Lithium is more than 2x faster than Rust panics on common Result-like usecases. See the benchmark.

See documentation for usage and installation instructions.


lib.rs:

Lightweight exceptions.

Lithium provides a custom exception mechanism as an alternative to Rust panics. Compared to Rust panics, this mechanism is allocation-free, avoids indirections and RTTI, and is hence faster, if less applicable.

On nightly, Lithium is more than 2x faster than Rust panics on common Result-like usecases. See the benchmark.

Usage

Throw an exception with throw, catch it with catch or the more low-level intercept. Unlike with Rust panics, non-Send and non-'static types can be used soundly.

Using the panic = "abort" strategy breaks Lithium; avoid doing that.

For interop, all crates that depend on Lithium need to use the same version:

[dependencies]
lithium = "1"

If you break either of these two requirements, cargo will scream at you.

Platform support

On stable Rust, Lithium uses the built-in panic mechanism, tweaking it to increase performance just a little bit.

On nightly Rust, Lithium uses a custom mechanism on the following targets:

Target Implementation Performance
Linux, macOS Itanium EH ABI 2.5x faster than panics
Windows (MSVC ABI) SEH 1.5x faster than panics
Windows (GNU ABI) Itanium EH ABI 2.5x faster than panics, but slower than MSVC
Emscripten (old EH) C++ exceptions 2x faster than panics
Emscripten (new EH) Wasm exceptions 2.5x faster than panics
WASI Itanium EH ABI 2.5x faster than panics

Lithium strives to support all targets that Rust panics support. If Lithium does not work correctly on such a target, please open an issue.

On nightly, Lithium can work without std on certain platforms that expose native thread locals and link in an Itanium-style unwinder. Such situations are best handled on a case-by-case basis: open an issue if you would like to see support for a certain std-less target.

Safety

Exceptions lack dynamic typing information. For soundness, the thrown and caught types must match exactly. Note that the functions are generic, and if the type is inferred wrong, UB will happen. Use turbofish to avoid this pitfall.

The matching types requirement only applies to exceptions that aren't caught inside the catch/intercept callback. For example, this is sound:

use lithium::{catch, throw};

struct A;
struct B;

unsafe {
    let _ = catch::<_, A>(|| {
        let _ = catch::<_, B>(|| throw(B));
        throw(A);
    });
}

The responsibility of upholding this safety requirement is split between the throwing and the catching functions. All throwing functions must be unsafe, listing "only caught by type E" as a safety requirement. All catching functions that take a user-supplied callback must be unsafe too, listing "callback only throws type E" as a safety requirement.

Although seemingly redundant, this enables safe abstractions over exceptions when both the throwing and the catching functions are provided by one crate. As long as the exception types used by the crate match, all safe user-supplied callbacks are sound to call, because safe callbacks can only interact with exceptions in an isolated manner.

Dependencies