Skip to content

Commit cbc1ad2

Browse files
authored
Merge pull request #415 from dtolnay/intodyn
Add 2 different conversions to `Box<dyn Error + Send + Sync + 'static>`
2 parents 29f2edd + e1a2017 commit cbc1ad2

File tree

1 file changed

+122
-8
lines changed

1 file changed

+122
-8
lines changed

src/error.rs

+122-8
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,8 @@ impl Error {
159159
object_mut: object_mut::<E>,
160160
#[cfg(any(feature = "std", not(anyhow_no_core_error)))]
161161
object_boxed: object_boxed::<E>,
162+
#[cfg(any(feature = "std", not(anyhow_no_core_error)))]
163+
object_reallocate_boxed: object_reallocate_boxed::<E>,
162164
object_downcast: object_downcast::<E>,
163165
#[cfg(anyhow_no_ptr_addr_of)]
164166
object_downcast_mut: object_downcast_mut::<E>,
@@ -188,6 +190,8 @@ impl Error {
188190
object_mut: object_mut::<MessageError<M>>,
189191
#[cfg(any(feature = "std", not(anyhow_no_core_error)))]
190192
object_boxed: object_boxed::<MessageError<M>>,
193+
#[cfg(any(feature = "std", not(anyhow_no_core_error)))]
194+
object_reallocate_boxed: object_reallocate_boxed::<MessageError<M>>,
191195
object_downcast: object_downcast::<M>,
192196
#[cfg(anyhow_no_ptr_addr_of)]
193197
object_downcast_mut: object_downcast_mut::<M>,
@@ -218,6 +222,8 @@ impl Error {
218222
object_mut: object_mut::<DisplayError<M>>,
219223
#[cfg(any(feature = "std", not(anyhow_no_core_error)))]
220224
object_boxed: object_boxed::<DisplayError<M>>,
225+
#[cfg(any(feature = "std", not(anyhow_no_core_error)))]
226+
object_reallocate_boxed: object_reallocate_boxed::<DisplayError<M>>,
221227
object_downcast: object_downcast::<M>,
222228
#[cfg(anyhow_no_ptr_addr_of)]
223229
object_downcast_mut: object_downcast_mut::<M>,
@@ -254,6 +260,8 @@ impl Error {
254260
object_mut: object_mut::<ContextError<C, E>>,
255261
#[cfg(any(feature = "std", not(anyhow_no_core_error)))]
256262
object_boxed: object_boxed::<ContextError<C, E>>,
263+
#[cfg(any(feature = "std", not(anyhow_no_core_error)))]
264+
object_reallocate_boxed: object_reallocate_boxed::<ContextError<C, E>>,
257265
object_downcast: context_downcast::<C, E>,
258266
#[cfg(anyhow_no_ptr_addr_of)]
259267
object_downcast_mut: context_downcast_mut::<C, E>,
@@ -284,6 +292,8 @@ impl Error {
284292
object_mut: object_mut::<BoxedError>,
285293
#[cfg(any(feature = "std", not(anyhow_no_core_error)))]
286294
object_boxed: object_boxed::<BoxedError>,
295+
#[cfg(any(feature = "std", not(anyhow_no_core_error)))]
296+
object_reallocate_boxed: object_reallocate_boxed::<BoxedError>,
287297
object_downcast: object_downcast::<Box<dyn StdError + Send + Sync>>,
288298
#[cfg(anyhow_no_ptr_addr_of)]
289299
object_downcast_mut: object_downcast_mut::<Box<dyn StdError + Send + Sync>>,
@@ -401,6 +411,8 @@ impl Error {
401411
object_mut: object_mut::<ContextError<C, Error>>,
402412
#[cfg(any(feature = "std", not(anyhow_no_core_error)))]
403413
object_boxed: object_boxed::<ContextError<C, Error>>,
414+
#[cfg(any(feature = "std", not(anyhow_no_core_error)))]
415+
object_reallocate_boxed: object_reallocate_boxed::<ContextError<C, Error>>,
404416
object_downcast: context_chain_downcast::<C>,
405417
#[cfg(anyhow_no_ptr_addr_of)]
406418
object_downcast_mut: context_chain_downcast_mut::<C>,
@@ -609,6 +621,98 @@ impl Error {
609621
}
610622
}
611623

624+
/// Convert to a standard library error trait object.
625+
///
626+
/// This is implemented as a cheap pointer cast that does not allocate or
627+
/// deallocate memory. Like [`anyhow::Error::from_boxed`], it's useful for
628+
/// interop with other error libraries.
629+
///
630+
/// The same conversion is also available as
631+
/// <code style="display:inline;white-space:normal;">impl From&lt;anyhow::Error&gt;
632+
/// for Box&lt;dyn Error + Send + Sync + &apos;static&gt;</code>.
633+
///
634+
/// If a backtrace was collected during construction of the `anyhow::Error`,
635+
/// that backtrace remains accessible using the standard library `Error`
636+
/// trait's provider API, but as a consequence, the resulting boxed error
637+
/// can no longer be downcast to its original underlying type.
638+
///
639+
/// ```
640+
#[cfg_attr(not(error_generic_member_access), doc = "# _ = stringify! {")]
641+
/// #![feature(error_generic_member_access)]
642+
///
643+
/// use anyhow::anyhow;
644+
/// use std::backtrace::Backtrace;
645+
/// use thiserror::Error;
646+
///
647+
/// #[derive(Error, Debug)]
648+
/// #[error("...")]
649+
/// struct MyError;
650+
///
651+
/// let anyhow_error = anyhow!(MyError);
652+
/// println!("{}", anyhow_error.backtrace()); // has Backtrace
653+
/// assert!(anyhow_error.downcast_ref::<MyError>().is_some()); // can downcast
654+
///
655+
/// let boxed_dyn_error = anyhow_error.into_boxed_dyn_error();
656+
/// assert!(std::error::request_ref::<Backtrace>(&*boxed_dyn_error).is_some()); // has Backtrace
657+
/// assert!(boxed_dyn_error.downcast_ref::<MyError>().is_none()); // can no longer downcast
658+
#[cfg_attr(not(error_generic_member_access), doc = "# };")]
659+
/// ```
660+
///
661+
/// [`anyhow::Error::from_boxed`]: Self::from_boxed
662+
#[cfg(any(feature = "std", not(anyhow_no_core_error)))]
663+
#[must_use]
664+
pub fn into_boxed_dyn_error(self) -> Box<dyn StdError + Send + Sync + 'static> {
665+
let outer = ManuallyDrop::new(self);
666+
unsafe {
667+
// Use vtable to attach ErrorImpl<E>'s native StdError vtable for
668+
// the right original type E.
669+
(vtable(outer.inner.ptr).object_boxed)(outer.inner)
670+
}
671+
}
672+
673+
/// Convert to a standard library error trait object.
674+
///
675+
/// Unlike `self.into_boxed_dyn_error()`, this method relocates the
676+
/// underlying error into a new allocation in order to make it downcastable
677+
/// to `&E` or `Box<E>` for its original underlying error type. Any
678+
/// backtrace collected during construction of the `anyhow::Error` is
679+
/// discarded.
680+
///
681+
/// ```
682+
#[cfg_attr(not(error_generic_member_access), doc = "# _ = stringify!{")]
683+
/// #![feature(error_generic_member_access)]
684+
///
685+
/// use anyhow::anyhow;
686+
/// use std::backtrace::Backtrace;
687+
/// use thiserror::Error;
688+
///
689+
/// #[derive(Error, Debug)]
690+
/// #[error("...")]
691+
/// struct MyError;
692+
///
693+
/// let anyhow_error = anyhow!(MyError);
694+
/// println!("{}", anyhow_error.backtrace()); // has Backtrace
695+
/// assert!(anyhow_error.downcast_ref::<MyError>().is_some()); // can downcast
696+
///
697+
/// let boxed_dyn_error = anyhow_error.reallocate_into_boxed_dyn_error_without_backtrace();
698+
/// assert!(std::error::request_ref::<Backtrace>(&*boxed_dyn_error).is_none()); // Backtrace lost
699+
/// assert!(boxed_dyn_error.downcast_ref::<MyError>().is_some()); // can downcast to &MyError
700+
/// assert!(boxed_dyn_error.downcast::<MyError>().is_ok()); // can downcast to Box<MyError>
701+
#[cfg_attr(not(error_generic_member_access), doc = "# };")]
702+
/// ```
703+
#[cfg(any(feature = "std", not(anyhow_no_core_error)))]
704+
#[must_use]
705+
pub fn reallocate_into_boxed_dyn_error_without_backtrace(
706+
self,
707+
) -> Box<dyn StdError + Send + Sync + 'static> {
708+
let outer = ManuallyDrop::new(self);
709+
unsafe {
710+
// Use vtable to attach E's native StdError vtable for the right
711+
// original type E.
712+
(vtable(outer.inner.ptr).object_reallocate_boxed)(outer.inner)
713+
}
714+
}
715+
612716
#[cfg(error_generic_member_access)]
613717
pub(crate) fn provide<'a>(&'a self, request: &mut Request<'a>) {
614718
unsafe { ErrorImpl::provide(self.inner.by_ref(), request) }
@@ -682,6 +786,8 @@ struct ErrorVTable {
682786
object_mut: unsafe fn(Mut<ErrorImpl>) -> &mut (dyn StdError + Send + Sync + 'static),
683787
#[cfg(any(feature = "std", not(anyhow_no_core_error)))]
684788
object_boxed: unsafe fn(Own<ErrorImpl>) -> Box<dyn StdError + Send + Sync + 'static>,
789+
#[cfg(any(feature = "std", not(anyhow_no_core_error)))]
790+
object_reallocate_boxed: unsafe fn(Own<ErrorImpl>) -> Box<dyn StdError + Send + Sync + 'static>,
685791
object_downcast: unsafe fn(Ref<ErrorImpl>, TypeId) -> Option<Ref<()>>,
686792
#[cfg(anyhow_no_ptr_addr_of)]
687793
object_downcast_mut: unsafe fn(Mut<ErrorImpl>, TypeId) -> Option<Mut<()>>,
@@ -752,6 +858,17 @@ where
752858
unsafe { unerased_own.boxed() }
753859
}
754860

861+
// Safety: requires layout of *e to match ErrorImpl<E>.
862+
#[cfg(any(feature = "std", not(anyhow_no_core_error)))]
863+
unsafe fn object_reallocate_boxed<E>(e: Own<ErrorImpl>) -> Box<dyn StdError + Send + Sync + 'static>
864+
where
865+
E: StdError + Send + Sync + 'static,
866+
{
867+
// Attach E's native StdError vtable.
868+
let unerased_own = e.cast::<ErrorImpl<E>>();
869+
Box::new(unsafe { unerased_own.boxed() }._object)
870+
}
871+
755872
// Safety: requires layout of *e to match ErrorImpl<E>.
756873
unsafe fn object_downcast<E>(e: Ref<ErrorImpl>, target: TypeId) -> Option<Ref<()>>
757874
where
@@ -1060,26 +1177,23 @@ where
10601177
impl From<Error> for Box<dyn StdError + Send + Sync + 'static> {
10611178
#[cold]
10621179
fn from(error: Error) -> Self {
1063-
let outer = ManuallyDrop::new(error);
1064-
unsafe {
1065-
// Use vtable to attach ErrorImpl<E>'s native StdError vtable for
1066-
// the right original type E.
1067-
(vtable(outer.inner.ptr).object_boxed)(outer.inner)
1068-
}
1180+
error.into_boxed_dyn_error()
10691181
}
10701182
}
10711183

10721184
#[cfg(any(feature = "std", not(anyhow_no_core_error)))]
10731185
impl From<Error> for Box<dyn StdError + Send + 'static> {
1186+
#[cold]
10741187
fn from(error: Error) -> Self {
1075-
Box::<dyn StdError + Send + Sync>::from(error)
1188+
error.into_boxed_dyn_error()
10761189
}
10771190
}
10781191

10791192
#[cfg(any(feature = "std", not(anyhow_no_core_error)))]
10801193
impl From<Error> for Box<dyn StdError + 'static> {
1194+
#[cold]
10811195
fn from(error: Error) -> Self {
1082-
Box::<dyn StdError + Send + Sync>::from(error)
1196+
error.into_boxed_dyn_error()
10831197
}
10841198
}
10851199

0 commit comments

Comments
 (0)