Expand description
§whereat - Lightweight error location tracking
150x faster than backtrace — production error tracing without debuginfo, panic, or overhead.
Error: UserNotFound
at src/db.rs:142:9
╰─ user_id = 42
at src/api.rs:89:5
╰─ in handle_request
at myapp @ https://github.com/you/myapp/blob/a1b2c3d/src/main.rs#L23§Try It Now
No setup required — just wrap errors with at() and propagate with .at():
use whereat::{at, At, ResultAtExt};
#[derive(Debug)]
enum MyError { NotFound }
fn inner() -> Result<(), At<MyError>> {
Err(at(MyError::NotFound)) // Wrap error, capture location
}
fn outer() -> Result<(), At<MyError>> {
inner().at_str("looking up user")?; // Add context
Ok(())
}
let err = outer().unwrap_err();
println!("{:?}", err); // Shows locations + context§Production Setup
For clickable GitHub links in traces, add one line to your crate root and use at!():
// In lib.rs or main.rs
whereat::define_at_crate_info!();
fn find_user(id: u64) -> Result<String, At<MyError>> {
Err(at!(MyError::NotFound)) // Now includes repo links in traces
}The at!() macro desugars to:
At::wrap(err)
.set_crate_info(crate::at_crate_info()) // Enables GitHub/GitLab links
.at() // Captures file:line:col§Which Approach?
| Situation | Use |
|---|---|
| Existing struct/enum you don’t want to modify | Wrap with At<YourError> |
| Want traces embedded inside your error type | Implement AtTraceable trait |
Wrapper approach (most common): Return Result<T, At<YourError>> from functions.
Embedded approach: Implement AtTraceable and store an AtTrace (or Box<AtTrace>)
field inside your error type. Return Result<T, YourError> directly.
§Starting a Trace
| Function | Crate info | Use when |
|---|---|---|
at(err) | ❌ None | Prototyping — no setup needed |
at!(err) | ✅ GitHub links | Production — requires define_at_crate_info!() |
err.start_at() | ❌ None | Chaining on Error trait types |
Start with at() to try things out. Upgrade to at!() before shipping — you’ll want
those clickable links when debugging production issues.
§Extending a Trace
Create a new location frame (call site is recorded):
| Method | Effect |
|---|---|
.at() | New frame with just file:line:col |
.at_fn(|| {}) | New frame + captures function name |
.at_named("step") | New frame + custom label |
Add context to the last frame (no new location):
| Method | Effect |
|---|---|
.at_str("msg") | Static string (zero-cost) |
.at_string(|| format!(...)) | Dynamic string (lazy) |
.at_data(|| value) | Typed via Display (lazy) |
.at_debug(|| value) | Typed via Debug (lazy) |
.at_error(source_err) | Attach a source error |
Key distinction: .at() creates a NEW frame. .at_str() and friends add to the LAST frame.
use whereat::{at, At, ResultAtExt};
#[derive(Debug)]
struct MyError;
fn example() -> Result<(), At<MyError>> {
// One frame with two contexts attached
let e = at(MyError).at_str("a").at_str("b");
assert_eq!(e.frame_count(), 1);
// Two frames: at() creates first, .at() creates second
let e = at(MyError).at().at_str("on second frame");
assert_eq!(e.frame_count(), 2);
Ok(())
}§Foreign Crates and Errors
When consuming errors from other crates, use at_crate!() to mark the boundary.
This ensures traces show your crate’s GitHub links, not confusing paths from dependencies.
whereat::define_at_crate_info!(); // Once in lib.rs
use whereat::{at_crate, At, ResultAtExt};
fn call_dependency() -> Result<(), At<DependencyError>> {
at_crate!(dependency::do_thing())?; // Marks crate boundary
Ok(())
}The at_crate!() macro desugars to:
result.at_crate(crate::at_crate_info()) // Adds boundary marker with your crate's infoFor plain errors without traces (e.g., std::io::Error), use map_err(at) to start tracing:
use whereat::{At, at, ResultAtExt};
fn external_api() -> Result<(), &'static str> {
Err("external error")
}
fn wrapper() -> Result<(), At<&'static str>> {
external_api().map_err(at).at_str("calling external API")?;
Ok(())
}§Design Goals
- Tiny overhead:
At<E>issizeof(E) + 8bytes; zero heap allocation on the Ok path - Zero-cost context:
.at_str("literal")stores a pointer, no copy or allocation - Lazy evaluation:
.at_string(|| ...)closures only run on error - no_std compatible: Works with just
core+alloc
§OOM Behavior
Trace allocations are fallible where possible — on OOM, trace entries are silently skipped
but your error E always propagates (it’s stored inline). See the README for details.
Modules§
- prelude
- Convenient re-exports for common usage.
Macros§
- at
- Start tracing an error with crate metadata for repository links.
- at_
crate - Add crate boundary marker to a Result with an
At<E>error. - define_
at_ crate_ info - Define crate-level error tracking info. Call once in your crate root (lib.rs or main.rs).
Structs§
- At
- An error with location tracking - wraps any error type.
- AtContext
Ref - A reference to context data attached to a trace location.
- AtCrate
Info - AtCrate
Info Builder - Builder for
AtCrateInfowith a fluent, const-compatible API. - AtFrame
- A single frame in a trace: location with its associated contexts.
- AtTrace
- Trace storage for location and context tracking.
Constants§
- AT_
MAX_ CONTEXTS - Maximum number of context entries in a trace.
- AT_
MAX_ FRAMES - Maximum number of location frames in a trace.
Traits§
- AtTraceable
- Trait for types that embed an
AtTracedirectly. - Error
AtExt - Extension trait that allows calling
.start_at()on error types. - Result
AtExt - Extension trait for adding location tracking to
Result<T, At<E>>. - Result
AtTraceable Ext - Extension trait for
Result<T, E>whereEimplementsAtTraceable.
Functions§
- at
- Wrap any value in
At<E>and capture the caller’s location.