4 ms·
It is a huge factor: you can't pattern match the original error anymore because its type is erased. You can no longer distinguish between different variants of
by vore 3y ago
It is a huge factor: you can't pattern match the original error anymore because its type is erased. You can no longer distinguish between different variants of an error enum, so you have to handle any kind of error the same way, which is the real shit experience.
- JoelJacobson 3y agoNot true, it's not "erased", it's wrapped. The original error is available via the `.inner` Wherr struct field.
- vore 3y agoConsider this: #[derive(Debug)] enum MyError { A, B, C } impl std::fmt::Display for MyError { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "{:?}", self) } } impl std::error::Error for MyError {} // Equivalent to wherr::Wherr. #[derive(Debug)] struct ErrorWrapper { inner: Box<dyn std::error::Error> } impl std::fmt::Display for ErrorWrapper { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { self.inner.fmt(f) } } impl std::error::Error for ErrorWrapper {} fn my_error() -> Result<(), MyError> { Err(MyError::A)? } // Equivalent to applying #[wherr] to my_error. fn my_error_wrapped() -> Result<(), ErrorWrapper> { Err(ErrorWrapper { inner: Box::new(MyError::A) })? } fn main() { // Works. match my_error().unwrap_err() { MyError::A => { println!("got MyError::A"); } _ => { println!("got some other MyError variant"); } } // Doesn't work: how do I match out the inner MyError? match my_error_wrapped().unwrap_err().inner { MyError::A => { println!("got MyError::A"); } _ => { println!("got some other MyError variant"); } } }
- JoelJacobson 3y ago// This will work: downcast the inner error and then match on it. match my_error_wrapped().unwrap_err().inner.downcast_ref::<MyError>() { Some(MyError::A) => println!("got MyError::A"), Some(_) => println!("got some other MyError variant"), None => println!("got an unknown error type"), }
- vore 3y agoI think, at that point, you should rather make Wherr be `struct Wherr<E> { inner: E, ... }` to avoid having to do a dynamic cast - I don't personally like the idea of erasing guarantees of what types you're getting.
- JoelJacobson 3y agoThat seems like a great idea. I've started a branch "attempt_to_avoid_dynamic_cast" where I've tried to implement your idea: https://github.com/joelonsql/wherr/commit/c5906a257fc9768ad0add455bbc89107d4e7d978 https://github.com/joelonsql/wherr/commit/c5906a257fc9768ad0... I couldn't figure out how to make it work though, since with these changes, now nested functions using the #[wherr] macro won't work due to "doesn't have a size known at compile-time". The other idea of just using #[track_caller] also seems promising, and will be considered for a future major version. But I still think it's interesting if the macro variant can be improved, to avoid having to do dynamic casts, hence this new branch.
- deleted 3y ago[deleted]
- JoelJacobson 3y agoIt is a valid problem though, since if you have code that currently depend on the different error enum variants, then that code needs to be modified to match on the original error via the `.inner` field instead.