Skip to content

Commit ea1aaef

Browse files
committed
Add 2 different conversions to Box<dyn Error + Send + Sync + 'static>
1 parent 213a9c2 commit ea1aaef

File tree

1 file changed

+110
-8
lines changed

1 file changed

+110
-8
lines changed

src/error.rs

Lines changed: 110 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@ impl Error {
158158
#[cfg(anyhow_no_ptr_addr_of)]
159159
object_mut: object_mut::<E>,
160160
object_boxed: object_boxed::<E>,
161+
object_reallocate_boxed: object_reallocate_boxed::<E>,
161162
object_downcast: object_downcast::<E>,
162163
#[cfg(anyhow_no_ptr_addr_of)]
163164
object_downcast_mut: object_downcast_mut::<E>,
@@ -186,6 +187,7 @@ impl Error {
186187
#[cfg(all(any(feature = "std", not(anyhow_no_core_error)), anyhow_no_ptr_addr_of))]
187188
object_mut: object_mut::<MessageError<M>>,
188189
object_boxed: object_boxed::<MessageError<M>>,
190+
object_reallocate_boxed: object_reallocate_boxed::<MessageError<M>>,
189191
object_downcast: object_downcast::<M>,
190192
#[cfg(anyhow_no_ptr_addr_of)]
191193
object_downcast_mut: object_downcast_mut::<M>,
@@ -215,6 +217,7 @@ impl Error {
215217
#[cfg(all(any(feature = "std", not(anyhow_no_core_error)), anyhow_no_ptr_addr_of))]
216218
object_mut: object_mut::<DisplayError<M>>,
217219
object_boxed: object_boxed::<DisplayError<M>>,
220+
object_reallocate_boxed: object_reallocate_boxed::<DisplayError<M>>,
218221
object_downcast: object_downcast::<M>,
219222
#[cfg(anyhow_no_ptr_addr_of)]
220223
object_downcast_mut: object_downcast_mut::<M>,
@@ -250,6 +253,7 @@ impl Error {
250253
#[cfg(anyhow_no_ptr_addr_of)]
251254
object_mut: object_mut::<ContextError<C, E>>,
252255
object_boxed: object_boxed::<ContextError<C, E>>,
256+
object_reallocate_boxed: object_reallocate_boxed::<ContextError<C, E>>,
253257
object_downcast: context_downcast::<C, E>,
254258
#[cfg(anyhow_no_ptr_addr_of)]
255259
object_downcast_mut: context_downcast_mut::<C, E>,
@@ -279,6 +283,7 @@ impl Error {
279283
#[cfg(anyhow_no_ptr_addr_of)]
280284
object_mut: object_mut::<BoxedError>,
281285
object_boxed: object_boxed::<BoxedError>,
286+
object_reallocate_boxed: object_reallocate_boxed::<BoxedError>,
282287
object_downcast: object_downcast::<Box<dyn StdError + Send + Sync>>,
283288
#[cfg(anyhow_no_ptr_addr_of)]
284289
object_downcast_mut: object_downcast_mut::<Box<dyn StdError + Send + Sync>>,
@@ -395,6 +400,7 @@ impl Error {
395400
#[cfg(all(any(feature = "std", not(anyhow_no_core_error)), anyhow_no_ptr_addr_of))]
396401
object_mut: object_mut::<ContextError<C, Error>>,
397402
object_boxed: object_boxed::<ContextError<C, Error>>,
403+
object_reallocate_boxed: object_reallocate_boxed::<ContextError<C, Error>>,
398404
object_downcast: context_chain_downcast::<C>,
399405
#[cfg(anyhow_no_ptr_addr_of)]
400406
object_downcast_mut: context_chain_downcast_mut::<C>,
@@ -603,6 +609,94 @@ impl Error {
603609
}
604610
}
605611

612+
/// Convert to a standard library error trait object.
613+
///
614+
/// This is implemented as a cheap pointer cast that does not allocate or
615+
/// deallocate memory. Like [`anyhow::Error::from_boxed`], it's useful for
616+
/// interop with other error libraries.
617+
///
618+
/// The same conversion is also available as
619+
/// <code style="display:inline;white-space:normal;">impl From&lt;anyhow::Error&gt;
620+
/// for Box&lt;dyn Error + Send + Sync + &apos;static&gt;</code>.
621+
///
622+
/// If a backtrace was collected during construction of the `anyhow::Error`,
623+
/// that backtrace remains accessible using the standard library `Error`
624+
/// trait's provider API, but as a consequence, the resulting boxed error
625+
/// can no longer be downcast to its original underlying type.
626+
///
627+
/// ```
628+
#[cfg_attr(not(error_generic_member_access), doc = "# const _: &str = stringify! {")]
629+
/// #![feature(error_generic_member_access)]
630+
///
631+
/// use anyhow::anyhow;
632+
/// use std::backtrace::Backtrace;
633+
/// use thiserror::Error;
634+
///
635+
/// #[derive(Error, Debug)]
636+
/// #[error("...")]
637+
/// struct MyError;
638+
///
639+
/// let anyhow_error = anyhow!(MyError);
640+
/// println!("{}", anyhow_error.backtrace()); // has Backtrace
641+
/// assert!(anyhow_error.downcast_ref::<MyError>().is_some()); // can downcast
642+
///
643+
/// let boxed_dyn_error = anyhow_error.into_boxed_dyn_error();
644+
/// assert!(std::error::request_ref::<Backtrace>(&*boxed_dyn_error).is_some()); // has Backtrace
645+
/// assert!(boxed_dyn_error.downcast_ref::<MyError>().is_none()); // can no longer downcast
646+
#[cfg_attr(not(error_generic_member_access), doc = "# };")]
647+
/// ```
648+
///
649+
/// [`anyhow::Error::from_boxed`]: Self::from_boxed
650+
pub fn into_boxed_dyn_error(self) -> Box<dyn StdError + Send + Sync + 'static> {
651+
let outer = ManuallyDrop::new(self);
652+
unsafe {
653+
// Use vtable to attach ErrorImpl<E>'s native StdError vtable for
654+
// the right original type E.
655+
(vtable(outer.inner.ptr).object_boxed)(outer.inner)
656+
}
657+
}
658+
659+
/// Convert to a standard library error trait object.
660+
///
661+
/// Unlike `self.into_boxed_dyn_error()`, this method relocates the
662+
/// underlying error into a new allocation in order to make it downcastable
663+
/// to `&E` or `Box<E>` for its original underlying error type. Any
664+
/// backtrace collected during construction of the `anyhow::Error` is
665+
/// discarded.
666+
///
667+
/// ```
668+
#[cfg_attr(not(error_generic_member_access), doc = "# const _: &str = stringify! {")]
669+
/// #![feature(error_generic_member_access)]
670+
///
671+
/// use anyhow::anyhow;
672+
/// use std::backtrace::Backtrace;
673+
/// use thiserror::Error;
674+
///
675+
/// #[derive(Error, Debug)]
676+
/// #[error("...")]
677+
/// struct MyError;
678+
///
679+
/// let anyhow_error = anyhow!(MyError);
680+
/// println!("{}", anyhow_error.backtrace()); // has Backtrace
681+
/// assert!(anyhow_error.downcast_ref::<MyError>().is_some()); // can downcast
682+
///
683+
/// let boxed_dyn_error = anyhow_error.reallocate_into_boxed_dyn_error_without_backtrace();
684+
/// assert!(std::error::request_ref::<Backtrace>(&*boxed_dyn_error).is_none()); // Backtrace lost
685+
/// assert!(boxed_dyn_error.downcast_ref::<MyError>().is_some()); // can downcast to &MyError
686+
/// assert!(boxed_dyn_error.downcast::<MyError>().is_ok()); // can downcast to Box<MyError>
687+
#[cfg_attr(not(error_generic_member_access), doc = "# };")]
688+
/// ```
689+
pub fn reallocate_into_boxed_dyn_error_without_backtrace(
690+
self,
691+
) -> Box<dyn StdError + Send + Sync + 'static> {
692+
let outer = ManuallyDrop::new(self);
693+
unsafe {
694+
// Use vtable to attach E's native StdError vtable for the right
695+
// original type E.
696+
(vtable(outer.inner.ptr).object_reallocate_boxed)(outer.inner)
697+
}
698+
}
699+
606700
#[cfg(error_generic_member_access)]
607701
pub(crate) fn provide<'a>(&'a self, request: &mut Request<'a>) {
608702
unsafe { ErrorImpl::provide(self.inner.by_ref(), request) }
@@ -675,6 +769,7 @@ struct ErrorVTable {
675769
#[cfg(all(any(feature = "std", not(anyhow_no_core_error)), anyhow_no_ptr_addr_of))]
676770
object_mut: unsafe fn(Mut<ErrorImpl>) -> &mut (dyn StdError + Send + Sync + 'static),
677771
object_boxed: unsafe fn(Own<ErrorImpl>) -> Box<dyn StdError + Send + Sync + 'static>,
772+
object_reallocate_boxed: unsafe fn(Own<ErrorImpl>) -> Box<dyn StdError + Send + Sync + 'static>,
678773
object_downcast: unsafe fn(Ref<ErrorImpl>, TypeId) -> Option<Ref<()>>,
679774
#[cfg(anyhow_no_ptr_addr_of)]
680775
object_downcast_mut: unsafe fn(Mut<ErrorImpl>, TypeId) -> Option<Mut<()>>,
@@ -744,6 +839,16 @@ where
744839
unsafe { unerased_own.boxed() }
745840
}
746841

842+
// Safety: requires layout of *e to match ErrorImpl<E>.
843+
unsafe fn object_reallocate_boxed<E>(e: Own<ErrorImpl>) -> Box<dyn StdError + Send + Sync + 'static>
844+
where
845+
E: StdError + Send + Sync + 'static,
846+
{
847+
// Attach E's native StdError vtable.
848+
let unerased_own = e.cast::<ErrorImpl<E>>();
849+
Box::new(unsafe { unerased_own.boxed() }._object)
850+
}
851+
747852
// Safety: requires layout of *e to match ErrorImpl<E>.
748853
unsafe fn object_downcast<E>(e: Ref<ErrorImpl>, target: TypeId) -> Option<Ref<()>>
749854
where
@@ -1051,24 +1156,21 @@ where
10511156
impl From<Error> for Box<dyn StdError + Send + Sync + 'static> {
10521157
#[cold]
10531158
fn from(error: Error) -> Self {
1054-
let outer = ManuallyDrop::new(error);
1055-
unsafe {
1056-
// Use vtable to attach ErrorImpl<E>'s native StdError vtable for
1057-
// the right original type E.
1058-
(vtable(outer.inner.ptr).object_boxed)(outer.inner)
1059-
}
1159+
error.into_boxed_dyn_error()
10601160
}
10611161
}
10621162

10631163
impl From<Error> for Box<dyn StdError + Send + 'static> {
1164+
#[cold]
10641165
fn from(error: Error) -> Self {
1065-
Box::<dyn StdError + Send + Sync>::from(error)
1166+
error.into_boxed_dyn_error()
10661167
}
10671168
}
10681169

10691170
impl From<Error> for Box<dyn StdError + 'static> {
1171+
#[cold]
10701172
fn from(error: Error) -> Self {
1071-
Box::<dyn StdError + Send + Sync>::from(error)
1173+
error.into_boxed_dyn_error()
10721174
}
10731175
}
10741176

0 commit comments

Comments
 (0)