-
Notifications
You must be signed in to change notification settings - Fork 13.6k
Description
As for today the following blanket implementation exists:
rust/library/alloc/src/boxed.rs
Lines 2443 to 2458 in e702534
#[stable(feature = "box_error", since = "1.8.0")] | |
impl<T: core::error::Error> core::error::Error for Box<T> { | |
#[allow(deprecated, deprecated_in_future)] | |
fn description(&self) -> &str { | |
core::error::Error::description(&**self) | |
} | |
#[allow(deprecated)] | |
fn cause(&self) -> Option<&dyn core::error::Error> { | |
core::error::Error::cause(&**self) | |
} | |
fn source(&self) -> Option<&(dyn core::error::Error + 'static)> { | |
core::error::Error::source(&**self) | |
} | |
} |
The problem with this is that T
is required to be Sized
, so, for example, Box<dyn Error>
doesn't implement Error
. It looks like a straightforward and intuitive assumption that Box<dyn Error>
should implement Error
trait, but it doesn't.
Going even further, there is an inconsistency between the blanket impls for Arc
as well, because there exists an analogous impl for Arc
that has relaxed Sized?
requirement:
rust/library/alloc/src/sync.rs
Lines 2769 to 2788 in e702534
#[stable(feature = "arc_error", since = "1.52.0")] | |
impl<T: core::error::Error + ?Sized> core::error::Error for Arc<T> { | |
#[allow(deprecated, deprecated_in_future)] | |
fn description(&self) -> &str { | |
core::error::Error::description(&**self) | |
} | |
#[allow(deprecated)] | |
fn cause(&self) -> Option<&dyn core::error::Error> { | |
core::error::Error::cause(&**self) | |
} | |
fn source(&self) -> Option<&(dyn core::error::Error + 'static)> { | |
core::error::Error::source(&**self) | |
} | |
fn provide<'a>(&'a self, req: &mut core::any::Demand<'a>) { | |
core::error::Error::provide(&**self, req); | |
} | |
} |
Use Case
The use case where I stumbled with this problem is where I wanted to put Box<dyn Error>
as a source
error in thiserror
error enum, but I couldn't because that type doesn't implement Error trait.
Workarounds
The workaround for this problem is to use Arc<T>
instead of Box<T>
when an Error impl is required for Sized?
type. It's also possible to convert Box<dyn Error>
into an Arc<dyn Error>
via this From
impl.
Activity
clubby789 commentedon Mar 27, 2023
@rustbot label +T-libs-api
richard-uk1 commentedon May 14, 2023
I may be wrong, but I think this is a known issue that can't be fixed because it would break backwards compatibility.
But if I'm wrong will other people please chime in. (It's much quicker to get an answer on the internet by posting something incorrect than by asking a question 😛)
waynr commentedon Aug 9, 2023
@derekdreery I came across this comment while working on #113464 which supports your backwards compatibility claim: https://github.com/rust-lang/rust/blob/master/library/core/src/error.rs#L409
But I'll admit I don't understand why removing theSized
bound via?Sized
is backwards incompatible and am interested to have that explained more clearly...Like if you had bounds ofMyTrait
andYourTrait
on a given generic type parameter and a concrete typeMyConcreteType
that implements both traits but remove theMyTrait
bound on the type parameter in question,MyConcreteType
should still be valid because it still implementsYourTrait
-- right? Intuitively, it seems to me that by removing the implicitSized
bound forT
inBox<T>
via?Sized
any concrete type loitering out there in the wild that isSized
should still be valid forimpl<T: core::error::Error + ?Sized> core::error::Error for Box<T>
.I think I just realized the issue here -- it's the downstream usages of Box (ie contexts receiving and using Box such as an error-reporting library like anyhow that rely on it having a size known at compile time) that potentially rely on the Sized bound which would be broken if it's removed, not the Box itself.
Amanieu commentedon Aug 11, 2023
I believe this is because
Box
is#[fundamental]
, which has implications for trait impls: it allows crates to implement traits onBox<MyType>
directly, which is not normally allowed for a crate that doesn't own theBox
type.It might still be acceptable to relax the bounds though, if a crater run shows no breakage.
waynr commentedon Aug 12, 2023
Okay I just tried this out:
But when I try compiling I get
So
impl<T: core::error::Error + ?Sized> core::error::Error for Box<T>
now means thatBox<dyn Error>
is anError
implementation which means thatimpl<T> From<T> for T
overlaps with the impls at line 2240 and 2207 inlibrary/alloc/src/boxed.rs
since they are basically now converting possibly from aBox<dyn Error>
to aBox<dyn Error>
. I find this a little bit confusing.Is this happening because of monomorphization? At compile time because one possible known implementation of
Error
isBox<Error + ?Sized>
akaBox<dyn Error>
which monomorphs (is that a word? is there such thing as monomorphin' time? (bad Power Rangers joke, can't help myself)) to (taking the impl at line 2207 oflibrary/alloc/src/boxed.rs
shown in the compiler error as an example) to something like:And this would also be monomorphed from
impl<T> From<T> for T
, hence the conflict.Is there a good way to work around this? Add a
Sized
bound to lines 2207 and 2240 inlibrary/alloc/src/boxed.rs
? When I try that, I get essentially the same error. 🤔anyhow
when writing customAssetLoader
s orAssetSaver
s bevyengine/bevy#10350Kek5chen commentedon May 19, 2025
This is also a thing i am currently stumbling over. This issue essentially forces you to implement your own type as the concrete type.
When trying to specify a trait type with bounds
Err: std::error::Error
this ends up blocking you from using aBox<dyn std::error::Error>
concrete. I was previously working around this by relaxing the trait type fromto
Which is enough in most cases, but some libraries like Snafu require you to implement the Error trait on your source types. Which doesn't match well for a library, trying to generically give users the ability to specify handler error types, that shouldn't have to satisfy this requirement.
It could be fixed downstream, but I don't think this is a downstream problem.
It literally takes away the ability to use Box in development for generic contexts under these conditions.
Veetaha commentedon May 19, 2025
@Kek5chen as a workaround you could also convert
Box<dyn Error>
into anArc<dyn Error>
(which does implementError
) via thisFrom
impl. I hope this helps!Kek5chen commentedon May 19, 2025
@Veetaha thanks, and I'm somewhat aware of this but I feel like this shouldn't have to be worked around.