1 unstable release
0.1.0 | Jul 25, 2024 |
---|
#1625 in Rust patterns
13KB
192 lines
traced_result
An proof-of-concept to automatically backtrace errors propagated with the ?
operator#
Note: This crate relies on the unstable try_trait_v2
language feature. This means it can only be used with the nightly
toolchain, may break at any time, and is thus not recommended for use in production code until this feature is stabilized.
Usage
traced_result
differs from crates like trace_error
in that it does not use macros to trace call stacks, but instead uses the (currently unstable) Try
trait to be as consistent with regular Result
s as possible.
The two types at the core of this crate are TracedResult<T, E>
, designed to work like std::result::Result<T, E>
, and TracedError<E>
, which is simply a wrapper around E
and a Vec<&'static Location<'static>>
. To get started, simply replace Result
with TracedResult
:
// From
fn foo() -> Result<Bar, Baz> {
// ...
return Err(Baz(/*...*/));
}
// To
fn foo() -> TracedResult<Bar, Baz> {
// ...
return TracedResult::Err(TracedError::new(Baz(/*...*/)))
}
TracedResult
and TracedError
also come with convenient From
implementations, allowing you to write something like
return TracedResult::Err(Baz(/*...*/).into())
or even
return Err(Baz(/*...*/)).into()
Now, whenever a TracedResult
is propagated with the ?
operator, TracedResult
's Try
impl will store the location of the operators usage to the errors call stack, if any:
fn foo() -> TracedResult<Bar, Baz> {
Err(Baz(/*...*/)).into()
}
fn do_something() -> TracedResult<(), Baz> {
let value = foo()?;
Ok(())
}
fn main() {
if let TracedResult::Err(error) = do_something() {
println!("{}", error)
// Baz at (40:21) in file example.rs
// at (2:26) in file example.rs
}
}
Result
methods
TracedResult<T, E>
currently has its the following methods:
unwrap()
and all related methods, including theunchecked
methodsis_ok()
andis_err()
map()
and all related methods- conversion to an
std::result::Result<T, TracedError<E>>
usinginto_result()
or theFrom
trait for compatibility any remaining methods – note that subsequent uses of the?
operator will no longer be tracked. To discard the call stack completely, you can also useTracedResult::discard_call_stack()
to get aResult<T, E>
without theTracedError
wrapper aroundE
.
Note: the #[track_caller]
attribute
Internally, TracedResult
uses the #[track_caller]
attribute to get the location at which the ?
operator was used. This means that if the result is propagated from a function which itself is annotated with #[track_caller]
, the Location
added to the call stack will be that of the function's caller, not that of the Try
operator itself.