-
Notifications
You must be signed in to change notification settings - Fork 13.3k
Add provider API to error trait #98072
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
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 |
---|---|---|
|
@@ -156,7 +156,7 @@ use core::array; | |
use core::convert::Infallible; | ||
|
||
use crate::alloc::{AllocError, LayoutError}; | ||
use crate::any::TypeId; | ||
use crate::any::{Demand, Provider, TypeId}; | ||
use crate::backtrace::Backtrace; | ||
use crate::borrow::Cow; | ||
use crate::cell; | ||
|
@@ -295,6 +295,85 @@ pub trait Error: Debug + Display { | |
fn cause(&self) -> Option<&dyn Error> { | ||
self.source() | ||
} | ||
|
||
/// Provides type based access to context intended for error reports. | ||
/// | ||
/// Used in conjunction with [`Demand::provide_value`] and [`Demand::provide_ref`] to extract | ||
/// references to member variables from `dyn Error` trait objects. | ||
/// | ||
/// # Example | ||
/// | ||
/// ```rust | ||
/// #![feature(provide_any)] | ||
/// #![feature(error_generic_member_access)] | ||
/// use core::fmt; | ||
/// use core::any::Demand; | ||
/// | ||
/// #[derive(Debug)] | ||
/// struct MyBacktrace { | ||
/// // ... | ||
/// } | ||
/// | ||
/// impl MyBacktrace { | ||
/// fn new() -> MyBacktrace { | ||
/// // ... | ||
/// # MyBacktrace {} | ||
/// } | ||
/// } | ||
/// | ||
/// #[derive(Debug)] | ||
/// struct SourceError { | ||
/// // ... | ||
/// } | ||
/// | ||
/// impl fmt::Display for SourceError { | ||
/// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
/// write!(f, "Example Source Error") | ||
/// } | ||
/// } | ||
/// | ||
/// impl std::error::Error for SourceError {} | ||
/// | ||
/// #[derive(Debug)] | ||
/// struct Error { | ||
/// source: SourceError, | ||
/// backtrace: MyBacktrace, | ||
/// } | ||
/// | ||
/// impl fmt::Display for Error { | ||
/// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
/// write!(f, "Example Error") | ||
/// } | ||
/// } | ||
/// | ||
/// impl std::error::Error for Error { | ||
/// fn provide<'a>(&'a self, req: &mut Demand<'a>) { | ||
/// req | ||
/// .provide_ref::<MyBacktrace>(&self.backtrace) | ||
/// .provide_ref::<dyn std::error::Error + 'static>(&self.source); | ||
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. Do you foresee people doing this? I'd probably discourage it as it's replicating I could also see people doing 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.
In the original PoC impl of error in core I thought it might be a good idea to deduplicate all of the provide style APIs to encourage a single point of interaction with the error trait, we could implement fn source(&self) -> Option<&(dyn Error + 'static)> {
core::any::request_by_type_tag::<'_, core::any::tags::Ref<dyn Error + 'static>, _>(self)
} So you'd still default to using 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 don't really see a problem with providing it via both, although it's somewhat odd. I think you might want to provide it as the concrete type in more cases? Regardless, I think landing this will give a chance for idioms to settle. |
||
/// } | ||
/// } | ||
/// | ||
/// fn main() { | ||
/// let backtrace = MyBacktrace::new(); | ||
/// let source = SourceError {}; | ||
/// let error = Error { source, backtrace }; | ||
/// let dyn_error = &error as &dyn std::error::Error; | ||
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. Why the choice of let a = error.request_ref::<MyBacktrace>().unwrap();
assert!(core::ptr::eq(&error.backtrace, a));
let dyn_error = &error as &dyn std::error::Error;
let b = dyn_error request_ref::<MyBacktrace>().unwrap();
assert!(core::ptr::eq(a, b)); or can you not call request_ref on a concrete type? If so, that'd make me sad. 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.
Not until we move error into core sadly, for now I had to impl the Provider trait directly on 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.
OK, if it can happen then, I'll be happy. |
||
/// let backtrace_ref = dyn_error.request_ref::<MyBacktrace>().unwrap(); | ||
/// | ||
/// assert!(core::ptr::eq(&error.backtrace, backtrace_ref)); | ||
/// } | ||
/// ``` | ||
#[unstable(feature = "error_generic_member_access", issue = "none")] | ||
#[allow(unused_variables)] | ||
fn provide<'a>(&'a self, req: &mut Demand<'a>) {} | ||
} | ||
|
||
#[unstable(feature = "error_generic_member_access", issue = "none")] | ||
impl Provider for dyn Error + 'static { | ||
fn provide<'a>(&'a self, req: &mut Demand<'a>) { | ||
self.provide(req) | ||
} | ||
} | ||
|
||
mod private { | ||
|
@@ -831,6 +910,18 @@ impl dyn Error + 'static { | |
None | ||
} | ||
} | ||
|
||
/// Request a reference of type `T` as context about this error. | ||
#[unstable(feature = "error_generic_member_access", issue = "none")] | ||
pub fn request_ref<T: ?Sized + 'static>(&self) -> Option<&T> { | ||
core::any::request_ref(self) | ||
} | ||
|
||
/// Request a value of type `T` as context about this error. | ||
#[unstable(feature = "error_generic_member_access", issue = "none")] | ||
pub fn request_value<T: 'static>(&self) -> Option<T> { | ||
core::any::request_value(self) | ||
} | ||
} | ||
|
||
impl dyn Error + 'static + Send { | ||
|
@@ -854,6 +945,18 @@ impl dyn Error + 'static + Send { | |
pub fn downcast_mut<T: Error + 'static>(&mut self) -> Option<&mut T> { | ||
<dyn Error + 'static>::downcast_mut::<T>(self) | ||
} | ||
|
||
/// Request a reference of type `T` as context about this error. | ||
#[unstable(feature = "error_generic_member_access", issue = "none")] | ||
pub fn request_ref<T: ?Sized + 'static>(&self) -> Option<&T> { | ||
<dyn Error + 'static>::request_ref(self) | ||
} | ||
|
||
/// Request a value of type `T` as context about this error. | ||
#[unstable(feature = "error_generic_member_access", issue = "none")] | ||
pub fn request_value<T: 'static>(&self) -> Option<T> { | ||
<dyn Error + 'static>::request_value(self) | ||
} | ||
} | ||
|
||
impl dyn Error + 'static + Send + Sync { | ||
|
@@ -877,6 +980,18 @@ impl dyn Error + 'static + Send + Sync { | |
pub fn downcast_mut<T: Error + 'static>(&mut self) -> Option<&mut T> { | ||
<dyn Error + 'static>::downcast_mut::<T>(self) | ||
} | ||
|
||
/// Request a reference of type `T` as context about this error. | ||
#[unstable(feature = "error_generic_member_access", issue = "none")] | ||
pub fn request_ref<T: ?Sized + 'static>(&self) -> Option<&T> { | ||
<dyn Error + 'static>::request_ref(self) | ||
} | ||
|
||
/// Request a value of type `T` as context about this error. | ||
#[unstable(feature = "error_generic_member_access", issue = "none")] | ||
pub fn request_value<T: 'static>(&self) -> Option<T> { | ||
<dyn Error + 'static>::request_value(self) | ||
} | ||
} | ||
|
||
impl dyn 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.
This turbofish is redundant, I believe, so I'd recommend removing it.
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.
The examples all use turbofish unconditionally to reduce the chance of accidentally erasing the member as an unexpected type, with the turbofish you'll get a compiler error if you don't provide that exact type.
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.
How would that occur? My understanding is that without the turbofish, the compiler will only be able to use the concrete type of the argument for type inference.
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.
by providing a type that is different from what you intended / expected, the example I gave in the other comment on source is the best example I have off of the top of my head. If you mean to provide something as a trait object but you don't turbofish the trait object type or remember to manually coerce it you'll end up providing the original unerased type, and then any attempt to request the trait object will fail at runtime.
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.
Right, but this is "accidentally forgetting to erase" while you said (emphasis mine)
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.
aah, yea, I overused erasing. In the first comment I was referring to
.provide_ref
as if it erased theT
that is being provided, but in reality its theDemand
that is the type erased memory location where theT
is stored, andprovide_ref
is acting more likedowncast
onAny
.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.
When I've played around with this API, I've always used explicit turbofish, and think it's a good practice so that you know what you're providing.