-
Notifications
You must be signed in to change notification settings - Fork 13k
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
Add downcasting to std::error::Error #24793
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -47,19 +47,22 @@ | |
// coherence challenge (e.g., specialization, neg impls, etc) we can | ||
// reconsider what crate these items belong in. | ||
|
||
use boxed::Box; | ||
use any::TypeId; | ||
use boxed::{self, Box}; | ||
use convert::From; | ||
use fmt::{self, Debug, Display}; | ||
use marker::{Send, Sync}; | ||
use marker::{Send, Sync, Reflect}; | ||
use mem::transmute; | ||
use num; | ||
use option::Option; | ||
use option::Option::None; | ||
use option::Option::{self, Some, None}; | ||
use result::Result::{self, Ok, Err}; | ||
use raw::TraitObject; | ||
use str; | ||
use string::{self, String}; | ||
|
||
/// Base functionality for all errors in Rust. | ||
#[stable(feature = "rust1", since = "1.0.0")] | ||
pub trait Error: Debug + Display { | ||
pub trait Error: Debug + Display + Reflect { | ||
/// A short description of the error. | ||
/// | ||
/// The description should not contain newlines or sentence-ending | ||
|
@@ -71,6 +74,14 @@ pub trait Error: Debug + Display { | |
/// The lower-level cause of this error, if any. | ||
#[stable(feature = "rust1", since = "1.0.0")] | ||
fn cause(&self) -> Option<&Error> { None } | ||
|
||
/// Get the `TypeId` of `self` | ||
#[doc(hidden)] | ||
#[unstable(feature = "core", | ||
reason = "unclear whether to commit to this public implementation detail")] | ||
fn type_id(&self) -> TypeId where Self: 'static { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you move the Reflect bound to a method-level where clause here, then we can use exclusively Any and avoid stabilizing Reflect. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm, actually this will prevent downcasting with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Drat, it was an interesting idea! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thought about this some more, and we can get away without stabilizing There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @reem Ah yes, I think at some point in the iterations on this topic I confused myself into thinking this would hurt more than it does: you can still have non- |
||
TypeId::of::<Self>() | ||
} | ||
} | ||
|
||
#[stable(feature = "rust1", since = "1.0.0")] | ||
|
@@ -154,3 +165,112 @@ impl Error for string::FromUtf16Error { | |
} | ||
} | ||
|
||
// copied from any.rs | ||
impl Error + 'static { | ||
/// Returns true if the boxed type is the same as `T` | ||
#[unstable(feature = "error_downcast", reason = "recently added")] | ||
#[inline] | ||
pub fn is<T: Error + 'static>(&self) -> bool { | ||
// Get TypeId of the type this function is instantiated with | ||
let t = TypeId::of::<T>(); | ||
|
||
// Get TypeId of the type in the trait object | ||
let boxed = self.type_id(); | ||
|
||
// Compare both TypeIds on equality | ||
t == boxed | ||
} | ||
|
||
/// Returns some reference to the boxed value if it is of type `T`, or | ||
/// `None` if it isn't. | ||
#[unstable(feature = "error_downcast", reason = "recently added")] | ||
#[inline] | ||
pub fn downcast_ref<T: Error + 'static>(&self) -> Option<&T> { | ||
if self.is::<T>() { | ||
unsafe { | ||
// Get the raw representation of the trait object | ||
let to: TraitObject = transmute(self); | ||
|
||
// Extract the data pointer | ||
Some(transmute(to.data)) | ||
} | ||
} else { | ||
None | ||
} | ||
} | ||
|
||
/// Returns some mutable reference to the boxed value if it is of type `T`, or | ||
/// `None` if it isn't. | ||
#[unstable(feature = "error_downcast", reason = "recently added")] | ||
#[inline] | ||
pub fn downcast_mut<T: Error + 'static>(&mut self) -> Option<&mut T> { | ||
if self.is::<T>() { | ||
unsafe { | ||
// Get the raw representation of the trait object | ||
let to: TraitObject = transmute(self); | ||
|
||
// Extract the data pointer | ||
Some(transmute(to.data)) | ||
} | ||
} else { | ||
None | ||
} | ||
} | ||
} | ||
|
||
impl Error + 'static + Send { | ||
/// Forwards to the method defined on the type `Any`. | ||
#[unstable(feature = "error_downcast", reason = "recently added")] | ||
#[inline] | ||
pub fn is<T: Error + 'static>(&self) -> bool { | ||
<Error + 'static>::is::<T>(self) | ||
} | ||
|
||
/// Forwards to the method defined on the type `Any`. | ||
#[unstable(feature = "error_downcast", reason = "recently added")] | ||
#[inline] | ||
pub fn downcast_ref<T: Error + 'static>(&self) -> Option<&T> { | ||
<Error + 'static>::downcast_ref::<T>(self) | ||
} | ||
|
||
/// Forwards to the method defined on the type `Any`. | ||
#[unstable(feature = "error_downcast", reason = "recently added")] | ||
#[inline] | ||
pub fn downcast_mut<T: Error + 'static>(&mut self) -> Option<&mut T> { | ||
<Error + 'static>::downcast_mut::<T>(self) | ||
} | ||
} | ||
|
||
impl Error { | ||
#[inline] | ||
#[unstable(feature = "error_downcast", reason = "recently added")] | ||
/// Attempt to downcast the box to a concrete type. | ||
pub fn downcast<T: Error + 'static>(self: Box<Self>) -> Result<Box<T>, Box<Error>> { | ||
if self.is::<T>() { | ||
unsafe { | ||
// Get the raw representation of the trait object | ||
let raw = boxed::into_raw(self); | ||
let to: TraitObject = | ||
transmute::<*mut Error, TraitObject>(raw); | ||
|
||
// Extract the data pointer | ||
Ok(Box::from_raw(to.data as *mut T)) | ||
} | ||
} else { | ||
Err(self) | ||
} | ||
} | ||
} | ||
|
||
impl Error + Send { | ||
#[inline] | ||
#[unstable(feature = "error_downcast", reason = "recently added")] | ||
/// Attempt to downcast the box to a concrete type. | ||
pub fn downcast<T: Error + 'static>(self: Box<Self>) -> Result<Box<T>, Box<Error + Send>> { | ||
let err: Box<Error> = self; | ||
<Error>::downcast(err).map_err(|s| unsafe { | ||
// reapply the Send marker | ||
transmute::<Box<Error>, Box<Error + Send>>(s) | ||
}) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,6 +13,7 @@ | |
use prelude::v1::*; | ||
use io::prelude::*; | ||
|
||
use marker::Reflect; | ||
use cmp; | ||
use error; | ||
use fmt; | ||
|
@@ -322,7 +323,7 @@ impl<W> From<IntoInnerError<W>> for Error { | |
} | ||
|
||
#[stable(feature = "rust1", since = "1.0.0")] | ||
impl<W: Send + fmt::Debug> error::Error for IntoInnerError<W> { | ||
impl<W: Reflect + Send + fmt::Debug> error::Error for IntoInnerError<W> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could this and the bounds used below change from There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have mixed feelings about this one. I decided to leave them as |
||
fn description(&self) -> &str { | ||
error::Error::description(self.error()) | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Better to just use Any here, the docs will be clearer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You have to use
Reflect
here to allowError
to implement downcasting without inheriting fromAny
.