Skip to main content

At

Struct At 

Source
pub struct At<E> { /* private fields */ }
Expand description

An error with location tracking - wraps any error type.

§Size

At<E> is sizeof(E) + 8 bytes on 64-bit platforms:

  • The error E is stored inline
  • The trace is boxed (8-byte pointer, null when empty)

§Equality and Hashing

At<E> implements PartialEq, Eq, and Hash based only on the inner error E, ignoring the trace. The trace is metadata about where an error was created, not what the error is.

This means two At<E> values are equal if their inner errors are equal, even if they were created at different source locations:

use whereat::at;

#[derive(Debug, PartialEq)]
struct MyError(u32);

let err1 = at(MyError(42));  // Created here
let err2 = at(MyError(42));  // Created on different line
assert_eq!(err1, err2);      // Equal because inner errors match

§Example

use whereat::{at, At};

#[derive(Debug)]
enum MyError { Oops }

// Create a traced error using at() function
let err: At<MyError> = at(MyError::Oops);
assert_eq!(err.frame_count(), 1);

§Note: Avoid At<At<E>>

Nesting At<At<E>> is supported but unnecessary and wasteful. Each At has its own trace, so nesting allocates two Box<AtTrace> instead of one. Use .at() on Results to extend the existing trace:

use whereat::{at, At};

#[derive(Debug)]
struct MyError;

// GOOD: Extend existing trace
fn good() -> Result<(), At<MyError>> {
    let err: At<MyError> = at(MyError);
    Err(err.at())  // Same trace, new location
}

// UNNECESSARY: Creates two separate traces
fn unnecessary() -> At<At<MyError>> {
    at(at(MyError))  // Two allocations
}

Implementations§

Source§

impl<E> At<E>

Source

pub const fn wrap(error: E) -> At<E>

Wrap an error without capturing any location.

Use this when you want to defer tracing until later (e.g., exiting a hot loop). Call .at() to add the first location when ready.

For normal use, prefer at() or at!() which capture the caller’s location immediately.

Source

pub fn from_parts(error: E, trace: AtTrace) -> At<E>

Create an At<E> from an error and an existing trace.

Used for transferring traces between error types.

Source

pub fn at(self) -> At<E>

Add the caller’s location to the trace.

This is the primary API for building up a stack trace as errors propagate. If allocation fails, the location is silently skipped.

§Example
use whereat::At;

#[derive(Debug)]
enum MyError { Oops }

fn inner() -> Result<(), At<MyError>> {
    Err(At::wrap(MyError::Oops).at())
}

fn outer() -> Result<(), At<MyError>> {
    inner().map_err(|e| e.at())
}
Source

pub fn at_fn<F>(self, _marker: F) -> At<E>
where F: Fn(),

Add a location frame with the caller’s function name as context.

Captures both file:line:col AND the function name at zero runtime cost. Pass an empty closure || {} - its type includes the parent function name.

§Example
use whereat::{at, At};

#[derive(Debug)]
enum MyError { NotFound }

fn load_config() -> Result<(), At<MyError>> {
    Err(at(MyError::NotFound).at_fn(|| {}))
}

// Output will include:
//     at src/lib.rs:10:5
//         in my_crate::load_config
Source

pub fn at_named(self, name: &'static str) -> At<E>

Add a location frame with an explicit name as context.

Like at_fn but with an explicit label instead of auto-detecting the function name. Useful for naming checkpoints, phases, or operations within a function.

§Example
use whereat::{at, At};

#[derive(Debug)]
enum MyError { Failed }

fn process() -> Result<(), At<MyError>> {
    // ... validation phase ...
    Err(at(MyError::Failed).at_named("validation"))
}

// Output will include:
//     at src/lib.rs:10:5
//         in validation
Source

pub fn at_str(self, msg: &'static str) -> At<E>

Add a static string context to the last location frame.

Does not add a new location frame - attaches context to the most recent frame in the trace. If the trace is empty, creates a frame at the caller’s location first.

Zero-cost for static strings - just stores a pointer. For dynamically-computed strings, use at_string().

§Frame behavior
use whereat::at;

#[derive(Debug)]
struct E;

// One frame with two contexts
let e = at(E).at_str("a").at_str("b");
assert_eq!(e.frame_count(), 1);

// Two frames: first from at(), second gets the context
let e = at(E).at().at_str("on second frame");
assert_eq!(e.frame_count(), 2);
§Example
use whereat::{at, At, ResultAtExt};

#[derive(Debug)]
enum MyError { IoError }

fn read_config() -> Result<(), At<MyError>> {
    Err(at(MyError::IoError))
}

fn init() -> Result<(), At<MyError>> {
    read_config().at_str("while loading configuration")?;
    Ok(())
}
Source

pub fn at_string(self, f: impl FnOnce() -> String) -> At<E>

Add a lazily-computed string context to the last location frame.

Does not add a new location frame - attaches context to the most recent frame in the trace. If the trace is empty, creates a frame at the caller’s location first.

The closure is only called on error path, avoiding allocation on success. For static strings, use at_str() instead for zero overhead.

§Example
use whereat::{at, At, ResultAtExt};

#[derive(Debug)]
enum MyError { NotFound }

fn load(path: &str) -> Result<(), At<MyError>> {
    Err(at(MyError::NotFound))
}

fn init(path: &str) -> Result<(), At<MyError>> {
    // Closure only runs on Err - no allocation on Ok path
    load(path).at_string(|| format!("loading {}", path))?;
    Ok(())
}
Source

pub fn at_data<T>(self, f: impl FnOnce() -> T) -> At<E>
where T: Display + Send + Sync + 'static,

Add lazily-computed typed context (Display) to the last location frame.

Does not add a new location frame - attaches context to the most recent frame in the trace. If the trace is empty, creates a frame at the caller’s location first.

The closure is only called on error path, avoiding allocation on success. Use for typed data that you want to format with Display and later retrieve via downcast_ref::<T>().

For plain string messages, prefer at_str() or at_string(). For Debug-formatted data, use at_debug().

§Example
use whereat::{at, At};

#[derive(Debug)]
enum MyError { NotFound }

// Custom Display type for rich context
struct PathContext(String);
impl std::fmt::Display for PathContext {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "path: {}", self.0)
    }
}

fn load(path: &str) -> Result<(), At<MyError>> {
    Err(at(MyError::NotFound))
}

fn init(path: &str) -> Result<(), At<MyError>> {
    load(path).map_err(|e| e.at_data(|| PathContext(path.into())))?;
    Ok(())
}
Source

pub fn at_debug<T>(self, f: impl FnOnce() -> T) -> At<E>
where T: Debug + Send + Sync + 'static,

Add lazily-computed typed context (Debug) to the last location frame.

Does not add a new location frame - attaches context to the most recent frame in the trace. If the trace is empty, creates a frame at the caller’s location first.

The closure is only called on error path, avoiding allocation on success. Use contexts() to retrieve entries and downcast_ref() to access typed data.

§Example
use whereat::at;

#[derive(Debug)]
struct RequestInfo { user_id: u64, path: String }

#[derive(Debug)]
enum MyError { Forbidden }

let err = at(MyError::Forbidden)
    .at_debug(|| RequestInfo { user_id: 42, path: "/admin".into() });

// Later, retrieve the context
for ctx in err.contexts() {
    if let Some(req) = ctx.downcast_ref::<RequestInfo>() {
        assert_eq!(req.user_id, 42);
    }
}
Source

pub fn at_error<Err>(self, err: Err) -> At<E>
where Err: Error + Send + Sync + 'static,

Add an error as context to the last location frame.

Does not add a new location frame - attaches context to the most recent frame in the trace. If the trace is empty, creates a frame at the caller’s location first.

Use this to attach a source error that implements core::error::Error. The error’s .source() chain is preserved and can be traversed.

§Example
use whereat::at;
use std::io;

#[derive(Debug)]
struct MyError;

fn wrap_io_error(io_err: io::Error) -> whereat::At<MyError> {
    at(MyError).at_error(io_err)
}
Source

pub fn at_crate(self, info: &'static AtCrateInfo) -> At<E>

Add a crate boundary marker to the last location frame.

Does not add a new location frame - attaches context to the most recent frame in the trace. If the trace is empty, creates a frame at the caller’s location first.

This marks that subsequent locations belong to a different crate, enabling correct GitHub links in cross-crate traces.

Requires define_at_crate_info!() or a custom at_crate_info() getter.

§Example
// Requires define_at_crate_info!() setup
use whereat::{at, At};

whereat::define_at_crate_info!();

#[derive(Debug)]
enum MyError { Wrapped(String) }

fn wrap_external_error(msg: &str) -> At<MyError> {
    at(MyError::Wrapped(msg.into()))
        .at_crate(crate::at_crate_info())
}
Source

pub fn set_crate_info(self, info: &'static AtCrateInfo) -> At<E>

Set the crate info for this trace.

This is used by at!() to provide repository metadata for GitHub links. Calling this creates the trace if it doesn’t exist yet.

§Example
// Requires define_at_crate_info!() setup
whereat::define_at_crate_info!();

#[derive(Debug)]
enum MyError { Oops }

let err = At::wrap(MyError::Oops)
    .set_crate_info(crate::at_crate_info())
    .at();
Source

pub fn crate_info(&self) -> Option<&'static AtCrateInfo>

Get the crate info for this trace, if set.

Source

pub fn error(&self) -> &E

Get a reference to the inner error.

Source

pub fn error_mut(&mut self) -> &mut E

Get a mutable reference to the inner error.

Source

pub fn into_inner(self) -> E

Consume self and return the inner error, discarding the trace.

Source

pub fn is_empty(&self) -> bool

Check if the trace is empty.

Source

pub fn contexts(&self) -> impl Iterator<Item = AtContextRef<'_>>

Iterate over all context entries, newest first.

Each call to at_str(), at_string(), at_data(), or at_debug() creates a context entry. Use AtContextRef methods to inspect context data.

Note: Prefer frames() for unified iteration over locations with their contexts.

§Example
use whereat::at;

#[derive(Debug)]
struct MyError;

let err = at(MyError)
    .at_str("loading config")
    .at_str("initializing");

let texts: Vec<_> = err.contexts()
    .filter_map(|ctx| ctx.as_text())
    .collect();
assert_eq!(texts, vec!["initializing", "loading config"]); // newest first
Source

pub fn frames(&self) -> impl Iterator<Item = AtFrame<'_>>

Iterate over frames (location + contexts pairs), oldest first.

This is the recommended way to traverse a trace. Each frame contains a location (or None for skipped-frames marker) and its associated contexts.

§Example
use whereat::at;

#[derive(Debug)]
struct MyError;

let err = at(MyError)
    .at_str("loading config")
    .at();

for frame in err.frames() {
    if let Some(loc) = frame.location() {
        println!("at {}:{}", loc.file(), loc.line());
    }
    for ctx in frame.contexts() {
        println!("  - {}", ctx);
    }
}
Source

pub fn frame_count(&self) -> usize

Get the number of frames in the trace.

Source

pub fn at_pop(&mut self) -> Option<AtFrameOwned>

Pop the most recent location and its contexts from the trace.

Returns None if the trace is empty.

Source

pub fn at_push(&mut self, segment: AtFrameOwned)

Push a segment (location + contexts) to the end of the trace.

Source

pub fn at_first_pop(&mut self) -> Option<AtFrameOwned>

Pop the oldest location and its contexts from the trace.

Returns None if the trace is empty.

Source

pub fn at_first_insert(&mut self, segment: AtFrameOwned)

Insert a segment (location + contexts) at the beginning of the trace.

Source

pub fn take_trace(&mut self) -> Option<AtTrace>

Take the entire trace, leaving self with an empty trace.

Source

pub fn set_trace(&mut self, trace: AtTrace)

Set the trace, replacing any existing trace.

Source

pub fn map_error<E2, F>(self, f: F) -> At<E2>
where F: FnOnce(E) -> E2,

Convert the error type while preserving the trace.

§Example
use whereat::{at, At};

#[derive(Debug)]
struct Error1;
#[derive(Debug)]
struct Error2;

impl From<Error1> for Error2 {
    fn from(_: Error1) -> Self { Error2 }
}

let err1: At<Error1> = at(Error1).at_str("context");
let err2: At<Error2> = err1.map_error(Error2::from);
assert_eq!(err2.frame_count(), 1);
Source

pub fn into_traceable<E2, F>(self, f: F) -> E2
where F: FnOnce(E) -> E2, E2: AtTraceable,

Convert to an AtTraceable type, transferring the trace.

The closure receives the inner error and should return an error type that implements AtTraceable. The trace is then transferred to the new error’s embedded trace.

§Example
use whereat::{at, At, AtTrace, AtTraceable};

#[derive(Debug)]
struct Inner;

struct MyError {
    trace: AtTrace,
}

impl AtTraceable for MyError {
    fn trace_mut(&mut self) -> &mut AtTrace { &mut self.trace }
    fn trace(&self) -> Option<&AtTrace> { Some(&self.trace) }
    fn fmt_message(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "my error")
    }
}

let at_err: At<Inner> = at(Inner).at_str("context");
let my_err: MyError = at_err.into_traceable(|_| MyError { trace: AtTrace::new() });
Source§

impl<E> At<E>
where E: Debug,

Source

pub fn display_with_meta(&self) -> impl Display

Format the error with GitHub links using AtCrateInfo from the trace.

When you use at!() or .at_crate(), the crate metadata is stored in the trace. This method uses that metadata to generate clickable GitHub links for each location.

For cross-crate traces, each at_crate() call updates the repository used for subsequent locations until another crate boundary is encountered.

§Example
// Requires define_at_crate_info!() setup
use whereat::{at, At};

whereat::define_at_crate_info!();

#[derive(Debug)]
struct MyError;

let err = at!(MyError);
println!("{}", err.display_with_meta());
Source§

impl<E> At<E>
where E: Display,

Source

pub fn full_trace(&self) -> impl Display

Format with full trace (message + locations + all contexts).

Returns a formatter that displays:

  • The error message (via Display)
  • All trace frame locations
  • All context strings at each location
§Example
use whereat::{at, At};

#[derive(Debug)]
struct MyError(&'static str);

impl std::fmt::Display for MyError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.0)
    }
}

let err: At<MyError> = at(MyError("failed")).at_str("loading config");
println!("{}", err.full_trace());
// Output:
// failed
//     at src/main.rs:10:1
//         loading config
Source

pub fn last_error_trace(&self) -> impl Display

Format with trace locations only (message + locations, no context strings).

Returns a formatter that displays:

  • The error message (via Display)
  • All trace frame locations
  • NO context strings (for compact output)
§Example
use whereat::{at, At};

#[derive(Debug)]
struct MyError(&'static str);

impl std::fmt::Display for MyError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.0)
    }
}

let err: At<MyError> = at(MyError("failed")).at_str("loading config");
println!("{}", err.last_error_trace());
// Output:
// failed
//     at src/main.rs:10:1
Source

pub fn last_error(&self) -> impl Display

Format just the error message (no trace).

Returns a formatter that only displays the error message via Display. Use this when you want to show the error without any trace information.

This is equivalent to using the Display impl directly.

Trait Implementations§

Source§

impl<E> AsRef<E> for At<E>

Source§

fn as_ref(&self) -> &E

Converts this type into a shared reference of the (usually inferred) input type.
Source§

impl<E> Debug for At<E>
where E: Debug,

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl<E> Display for At<E>
where E: Display,

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl<E> Error for At<E>
where E: Error,

Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl<E> From<E> for At<E>

Source§

fn from(error: E) -> At<E>

Converts to this type from the input type.
Source§

impl<E> Hash for At<E>
where E: Hash,

Source§

fn hash<H>(&self, state: &mut H)
where H: Hasher,

Hash only the inner error, not the trace.

Consistent with PartialEq: the trace is metadata, not identity.

1.3.0 · Source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where H: Hasher, Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
Source§

impl<E> PartialEq for At<E>
where E: PartialEq,

Source§

fn eq(&self, other: &At<E>) -> bool

Compare two At<E> errors by their inner error only.

The trace is metadata about where the error was created, not what the error is. Two errors with the same E value are equal regardless of their traces.

1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
Source§

impl<E> Eq for At<E>
where E: Eq,

Auto Trait Implementations§

§

impl<E> Freeze for At<E>
where E: Freeze,

§

impl<E> !RefUnwindSafe for At<E>

§

impl<E> Send for At<E>
where E: Send,

§

impl<E> Sync for At<E>
where E: Sync,

§

impl<E> Unpin for At<E>
where E: Unpin,

§

impl<E> !UnwindSafe for At<E>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<Q, K> Equivalent<K> for Q
where Q: Eq + ?Sized, K: Borrow<Q> + ?Sized,

Source§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
Source§

impl<E> ErrorAtExt for E
where E: Error,

Source§

fn start_at(self) -> At<E>

Wrap this value in At<E> and add the caller’s location. If allocation fails, the error is still wrapped but trace may be empty. Read more
Source§

impl<T> From<!> for T

Source§

fn from(t: !) -> T

Converts to this type from the input type.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToString for T
where T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.