Allow unboxing `source`
This program fails to downcast its source errors, since they are Box<MyError>, not MyError. As briefly mentioned on discord.
use snafu::ResultExt as _;
#[derive(Debug, snafu::Snafu)]
pub enum MyError {
#[snafu(display("error: {error_code:08X}"))]
LeafError { error_code: u32 },
#[snafu(display("at depth {depth}"))]
NodeError {
depth: u32,
#[snafu(source(from(MyError, Box::new)))]
source: Box<MyError>
},
}
fn main() {
let e = fake_fn(3).unwrap_err();
for e in std::iter::successors(Some(&e as &dyn std::error::Error), |e| e.source()) {
if let Some(e) = e.downcast_ref::<MyError>() {
println!("downcasted: {}", e);
} else {
println!("failed: {}", e);
}
}
}
fn fake_fn(depth: u32) -> Result<(), MyError> {
if depth == 0 {
LeafSnafu { error_code: 0xDEADBEEFu32 }.fail()
} else {
fake_fn(depth - 1).context(NodeSnafu { depth })
}
}
How does this syntax feel?
#[derive(Debug, Snafu)]
enum RecursiveError {
Leaf,
Node {
#[snafu(source(from(RecursiveError, Box::new)))]
#[snafu(source(via(|e| &**e)))]
source: Box<RecursiveError>,
},
}
I don't love via as the keyword; happy to entertain alternatives.
Oh, it's time to shed the bike? Some other alternatives might be into and as. into is nice as a counterpart to from, but usually into is for value-to-value while as is for ref-to-ref. But as is a keyword, so that doesn't feel great either, even if macros aren't bound by rust keywords.
Somewhat relatedly, a source(boxed) attribute might be nice, as a shorthand for source(from(T, Box::new), blah(|e| &**e)).
But
asis a keyword, so that doesn't feel great either, even if macros aren't bound by rust keywords.
I don't mind the as, especially as it's going to be namespaced by the source(). I was going to say that we already use mod, but I guess that's module. I think I originally reached for as as well, then picked via as a quick synonym that would be easy to grep for to change it 😉
source(boxed)attribute might be nice
It'd have to be boxed(T), and it's annoying that you can't access ::alloc::boxed::Box in a non-no_std program like you can access ::core::....
It'd have to be
boxed(T)
My thought was that it'd scrape out the inner type from the path, but that's pretty unreliable especially with type aliases. So yeah, boxed(T) is better.
and it's annoying that you can't access
::alloc::boxed::Box
But strictly speaking you don't need to. Could make it work with any From<T> + Deref<Target=T>.
I think in french via is exactly right :p
If I get it it's just a inline map no ?