-
Notifications
You must be signed in to change notification settings - Fork 12.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Error causes can't be downcast #35943
Comments
cc @Stebalien |
cc me |
This is definitely a bug in the trait as written. OTOH, it's a hard one to fix. Just adding the I did have one thought -- though it probably won't work -- which is adding |
Another possibility might be deprecating the existing method and adding a new one, I guess (with a default). But it'll be a drag to deal with, particularly since there is no way to "bridge" from the old implementation. |
cc me |
I think what happened here is that we had It's definitely worth a crater run with adding a |
So, I'm worried about generic errors that return values to the programmer (e.g. Unfortunately, this logic does not apply to errors that return values of non-generic types that just happen to not have static lifetimes. |
Possible routes to solve:
We'll be using crater here to figure out what to possibly do here. |
I don't think we can replace the I also don't think we can add |
This issue happens even when not using use std::error::Error;
use std::fmt::{self, Display, Formatter};
#[derive(Debug)]
struct MyError { }
impl Error for MyError {
fn description(&self) -> &str {
"description"
}
}
impl Display for MyError {
fn fmt(&self, formatter: &mut Formatter) -> Result<(), fmt::Error> {
write!(formatter, "description")
}
}
fn main() {
let error: Box<Error> = From::from(Box::new(MyError {}));
let downcasted = error.downcast_ref::<MyError>();
println!("{:?}", downcasted); // WRONG: prints None.
let error: Box<Error> = Box::new(MyError {});
let downcasted = error.downcast_ref::<MyError>();
println!("{:?}", downcasted); // OK: prints Some(MyError).
} |
@antoyo That's because you're double boxing. The correct code is: use std::error::Error;
use std::fmt::{self, Display, Formatter};
#[derive(Debug)]
struct MyError { }
impl Error for MyError {
fn description(&self) -> &str {
"description"
}
}
impl Display for MyError {
fn fmt(&self, formatter: &mut Formatter) -> Result<(), fmt::Error> {
write!(formatter, "description")
}
}
fn main() {
let error: Box<Error> = From::from(Box::new(MyError {}));
let downcasted = error.downcast_ref::<Box<MyError>>(); // Downcast to `Box<MyError>`
println!("{:?}", downcasted);
let error: Box<Error> = Box::new(MyError {});
let downcasted = error.downcast_ref::<MyError>();
println!("{:?}", downcasted); // OK: prints Some(MyError).
} However, this is extremely confusing and magical. |
@alexcrichton yes, I was afraid it would come to that. I wonder if we could add a new method and deprecate the old one somehow...or something. |
Would it make more sense to fix down casting instead? After reading about the issue, it seems like downcasting really should work for any type unless that type has invariant lifetime parameters. The implementation would need to squash the covariant parameters to their smallest possible lifetimes (i.e. that of the Error itself) and contravariant lifetime to the largest ( This wouldn't even need a constraint like Any has. I see no problem with letting literally all types implement a trait that did this -- there would just be a handful of types (those with invariant lifetime parameters) that you couldn't downcast to, and those types would give nice errors because they're not DowncastableFrom. |
I don't think those lifetime adjustments can be expressed in the type system. |
Why not? This compiles, and I think that, with appropriate compiler support ( /// This is automatically implemented for all types T for which downcasting
/// a type-erased reference of lifetime `'source` to `&'source T` is sound.
/// For example, a simple struct with a lifetime parameter `'a` containing
/// references of lifetime `'a` is `DowncastableFrom<'a>`.
///
/// NB: I don't think this is the same thing as `T: 'a`, but
/// I haven't thought about it too hard.
pub trait DowncastableFrom<'source> {}
pub trait ImprovedAny {
fn downcast<'a, T>(&'a self) -> &'a T where T: DowncastableFrom<'a>;
} |
Bumped into this trying to make reqwest's error type a struct instead of an enum, with the idea that people could downcast the |
Just hit this trying to bring error-chain into cargo. cargo wants to decide whether to show an error in the chain to the user based on the nature of the error. |
I'm not sure if my observation is right but isn't it impossible to return a I might have missed something crucial but assuming I didn't couldn't we add a impl Error+'static {
fn downcast_cause<T: Error+'static>(&self) -> Option<&T> {
self.cause()
.map(|err| { mem::transmute::<&Error, &(Error+'static)>(err) })
.and_then( |err| err.downcast_ref::<T>() )
}
} I just had this idea and probably missed some points like that you still have to have something like PS: |
Interesting thought. Something like this might indeed work. |
Thankfully the extern crate failure;
use failure::Fail;
fn cause_is_io_error(error: &Fail) -> bool {
match error.cause() {
Some(cause) => cause.downcast_ref::<std::io::Error>().is_some(),
None => false,
}
} |
I am closing this issue because I don't believe it is actionable as is (beyond sitting on the Rust 2.0 wishlist). Any solution we implement in the Error trait or elsewhere will need to go through the RFC process. @dathinab or anyone else who would be interested in spearheading the RFC for this -- it would be great to see an RFC with the |
For anyone coming to this issue via old links or search results, know that this has long since been fixed with RFC 2504. (This blog post has additional context.) |
Originally reported here: https://users.rust-lang.org/t/cant-to-introspect-error-cause/6536/2
Because
cause
doesn't return anError
bound by'static
you can't downcast it.This seems like a significant oversight. Of course you want to be able to downcast that object.
The text was updated successfully, but these errors were encountered: