Modern, flat, zero-cost error flow based on pure struct errors.
This crate was AI-generated and has not undergone human review. It likely contains bugs and should NOT be used in production. The API is subject to change after audit.
Traditional Rust error handling forces you to wrap everything in Result and nest errors inside enum variants. struct_error inverts this model:
- Errors are first-class structs, not enum variants.
- No manual
Ok/Errwrapping inside#[throws]functions. - Pattern match by type name without destructuring nested
Result/Enumlayers. - Zero runtime cost: everything is resolved at compile time via procedural macros.
#[error]— Define atomic error structs with auto-derivedDebug,Display, andError.#[united_error]— Create compile-time aliases for error sets.#[throws]— Implicitly returnResult<T, Unt<...>>and rewrite control flow.match_error!— Blind, type-driven pattern matching on errors.throw!— Explicitly throw an error inside a#[throws]function.
Add struct_error to your Cargo.toml:
[dependencies]
struct_error = "0.0.1"use struct_error::error;
#[error("resource not found: {}", id)]
pub struct NotFound {
pub id: u64,
}
#[error("connection timed out after {}ms", ms)]
pub struct Timeout {
pub ms: u64,
}use struct_error::united_error;
#[united_error(NotFound, Timeout)]
pub struct AppError;use struct_error::{throws, throw};
#[throws(NotFound, Timeout)]
pub fn fetch_resource(id: u64) -> String {
if id == 0 {
throw!(NotFound { id });
}
if id == 99 {
throw!(Timeout { ms: 5000 });
}
format!("resource-{}", id)
}
#[throws(NotFound, Timeout)]
pub fn process(id: u64) -> String {
let res = fetch_resource(id)?; // ? propagates automatically
res.to_uppercase()
}use struct_error::match_error;
fn main() {
let result = process(0);
match_error!(result {
Ok(v) => println!("success: {}", v),
NotFound { id } => println!("not found: {}", id),
Timeout { ms } => println!("timeout: {}ms", ms),
});
}#[throws] and match_error! must agree on the exact nesting order of the implicit Unt HList. This is achieved by a blind sorting algorithm that sorts error type paths lexicographically by their tail segment first, then walks backward. If two paths share the same tail but differ in qualification (e.g. Timeout vs db::Timeout), compilation aborts with an ambiguity error. More detailed path must be provided (e.g. net::Timeout vs db::Timeout).
At runtime, the error union is represented as a nested enum:
pub enum Unt<H, T> {
Here(H),
There(T),
}For three errors A, B, C, the return type becomes:
Result<T, Unt<A, Unt<B, Unt<C, End>>>>where End is an uninhabited terminator that enables exhaustiveness checking.
#[throws] expands in two phases:
- Phase 1 (
throwsproc macro): Usesmacro_magic::forward_tokens!to collect the AST tokens of each error type. - Phase 2 (
__throws_impl): Receives the collected tokens, deduplicates and sorts the error paths, generates theUntreturn type, injects the local__StructErrorIntotrait, and rewrites the function body AST to intercept?and wrap returns inOk.
Licensed under either of:
at your option.