Join GitHub today
GitHub is home to over 31 million developers working together to host and review code, manage projects, and build software together.
Sign upError causes can't be downcast #35943
Comments
brson
added
T-libs
rust-2-breakage-wishlist
labels
Aug 23, 2016
This comment has been minimized.
This comment has been minimized.
|
cc @Stebalien |
This comment has been minimized.
This comment has been minimized.
|
cc me |
This comment has been minimized.
This comment has been minimized.
|
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 |
This comment has been minimized.
This comment has been minimized.
|
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. |
This comment has been minimized.
This comment has been minimized.
|
cc me |
This comment has been minimized.
This comment has been minimized.
|
I think what happened here is that we had It's definitely worth a crater run with adding a |
aturon
added
the
I-nominated
label
Aug 25, 2016
This comment has been minimized.
This comment has been minimized.
|
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. |
This comment has been minimized.
This comment has been minimized.
|
Possible routes to solve:
We'll be using crater here to figure out what to possibly do here. |
alexcrichton
removed
the
I-nominated
label
Sep 12, 2016
This comment has been minimized.
This comment has been minimized.
|
I don't think we can replace the I also don't think we can add |
This comment has been minimized.
This comment has been minimized.
|
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).
} |
This comment has been minimized.
This comment has been minimized.
|
@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. |
This comment has been minimized.
This comment has been minimized.
|
@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. |
This comment has been minimized.
This comment has been minimized.
amluto
commented
Sep 30, 2016
|
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. |
This comment has been minimized.
This comment has been minimized.
|
I don't think those lifetime adjustments can be expressed in the type system. |
This comment has been minimized.
This comment has been minimized.
amluto
commented
Sep 30, 2016
|
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>;
} |
This comment has been minimized.
This comment has been minimized.
|
Bumped into this trying to make reqwest's error type a struct instead of an enum, with the idea that people could downcast the |
This comment has been minimized.
This comment has been minimized.
jluner
commented
May 21, 2017
|
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. |
This comment has been minimized.
This comment has been minimized.
rustonaut
commented
May 22, 2017
•
|
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: |
This comment has been minimized.
This comment has been minimized.
Interesting thought. Something like this might indeed work. |
Mark-Simulacrum
added
the
C-feature-request
label
Jul 26, 2017
This comment has been minimized.
This comment has been minimized.
|
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,
}
} |
This comment has been minimized.
This comment has been minimized.
|
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 |
brson commentedAug 23, 2016
•
edited
Originally reported here: https://users.rust-lang.org/t/cant-to-introspect-error-cause/6536/2
Because
causedoesn't return anErrorbound by'staticyou can't downcast it.This seems like a significant oversight. Of course you want to be able to downcast that object.