Skip to content

Commit d5c5de2

Browse files
chescockmockersf
authored andcommitted
Use Display instead of Debug in the default error handler (#18629)
# Objective Improve error messages for missing resources. The default error handler currently prints the `Debug` representation of the error type instead of `Display`. Most error types use `#[derive(Debug)]`, resulting in a dump of the structure, but will have a user-friendly message for `Display`. Follow-up to #18593 ## Solution Change the default error handler to use `Display` instead of `Debug`. Change `BevyError` to include the backtrace in the `Display` format in addition to `Debug` so that it is still included. ## Showcase Before: ``` Encountered an error in system `system_name`: SystemParamValidationError { skipped: false, message: "Resource does not exist", param: "bevy_ecs::change_detection::Res<app_name::ResourceType>" } Encountered an error in system `other_system_name`: "String message with\nmultiple lines." ``` After ``` Encountered an error in system `system_name`: Parameter `Res<ResourceType>` failed validation: Resource does not exist Encountered an error in system `other_system_name`: String message with multiple lines. ```
1 parent d5d57bb commit d5c5de2

File tree

4 files changed

+48
-42
lines changed

4 files changed

+48
-42
lines changed

crates/bevy_ecs/src/error/bevy_error.rs

Lines changed: 45 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -34,48 +34,11 @@ impl BevyError {
3434
pub fn downcast_ref<E: Error + 'static>(&self) -> Option<&E> {
3535
self.inner.error.downcast_ref::<E>()
3636
}
37-
}
38-
39-
/// This type exists (rather than having a `BevyError(Box<dyn InnerBevyError)`) to make [`BevyError`] use a "thin pointer" instead of
40-
/// a "fat pointer", which reduces the size of our Result by a usize. This does introduce an extra indirection, but error handling is a "cold path".
41-
/// We don't need to optimize it to that degree.
42-
/// PERF: We could probably have the best of both worlds with a "custom vtable" impl, but thats not a huge priority right now and the code simplicity
43-
/// of the current impl is nice.
44-
struct InnerBevyError {
45-
error: Box<dyn Error + Send + Sync + 'static>,
46-
#[cfg(feature = "backtrace")]
47-
backtrace: std::backtrace::Backtrace,
48-
}
49-
50-
// NOTE: writing the impl this way gives us From<&str> ... nice!
51-
impl<E> From<E> for BevyError
52-
where
53-
Box<dyn Error + Send + Sync + 'static>: From<E>,
54-
{
55-
#[cold]
56-
fn from(error: E) -> Self {
57-
BevyError {
58-
inner: Box::new(InnerBevyError {
59-
error: error.into(),
60-
#[cfg(feature = "backtrace")]
61-
backtrace: std::backtrace::Backtrace::capture(),
62-
}),
63-
}
64-
}
65-
}
66-
67-
impl Display for BevyError {
68-
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
69-
writeln!(f, "{}", self.inner.error)?;
70-
Ok(())
71-
}
72-
}
7337

74-
impl Debug for BevyError {
75-
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
76-
writeln!(f, "{:?}", self.inner.error)?;
38+
fn format_backtrace(&self, _f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
7739
#[cfg(feature = "backtrace")]
7840
{
41+
let f = _f;
7942
let backtrace = &self.inner.backtrace;
8043
if let std::backtrace::BacktraceStatus::Captured = backtrace.status() {
8144
let full_backtrace = std::env::var("BEVY_BACKTRACE").is_ok_and(|val| val == "full");
@@ -123,7 +86,50 @@ impl Debug for BevyError {
12386
}
12487
}
12588
}
89+
Ok(())
90+
}
91+
}
92+
93+
/// This type exists (rather than having a `BevyError(Box<dyn InnerBevyError)`) to make [`BevyError`] use a "thin pointer" instead of
94+
/// a "fat pointer", which reduces the size of our Result by a usize. This does introduce an extra indirection, but error handling is a "cold path".
95+
/// We don't need to optimize it to that degree.
96+
/// PERF: We could probably have the best of both worlds with a "custom vtable" impl, but thats not a huge priority right now and the code simplicity
97+
/// of the current impl is nice.
98+
struct InnerBevyError {
99+
error: Box<dyn Error + Send + Sync + 'static>,
100+
#[cfg(feature = "backtrace")]
101+
backtrace: std::backtrace::Backtrace,
102+
}
103+
104+
// NOTE: writing the impl this way gives us From<&str> ... nice!
105+
impl<E> From<E> for BevyError
106+
where
107+
Box<dyn Error + Send + Sync + 'static>: From<E>,
108+
{
109+
#[cold]
110+
fn from(error: E) -> Self {
111+
BevyError {
112+
inner: Box::new(InnerBevyError {
113+
error: error.into(),
114+
#[cfg(feature = "backtrace")]
115+
backtrace: std::backtrace::Backtrace::capture(),
116+
}),
117+
}
118+
}
119+
}
126120

121+
impl Display for BevyError {
122+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
123+
writeln!(f, "{}", self.inner.error)?;
124+
self.format_backtrace(f)?;
125+
Ok(())
126+
}
127+
}
128+
129+
impl Debug for BevyError {
130+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
131+
writeln!(f, "{:?}", self.inner.error)?;
132+
self.format_backtrace(f)?;
127133
Ok(())
128134
}
129135
}

crates/bevy_ecs/src/error/handler.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ pub fn default_error_handler() -> fn(BevyError, ErrorContext) {
127127
macro_rules! inner {
128128
($call:path, $e:ident, $c:ident) => {
129129
$call!(
130-
"Encountered an error in {} `{}`: {:?}",
130+
"Encountered an error in {} `{}`: {}",
131131
$c.kind(),
132132
$c.name(),
133133
$e

crates/bevy_ecs/src/system/system_param.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2932,7 +2932,7 @@ mod tests {
29322932
}
29332933

29342934
#[test]
2935-
#[should_panic = "Encountered an error in system `bevy_ecs::system::system_param::tests::missing_resource_error::res_system`: SystemParamValidationError { skipped: false, message: \"Resource does not exist\", param: \"bevy_ecs::change_detection::Res<bevy_ecs::system::system_param::tests::missing_resource_error::MissingResource>\" }"]
2935+
#[should_panic = "Encountered an error in system `bevy_ecs::system::system_param::tests::missing_resource_error::res_system`: Parameter `Res<MissingResource>` failed validation: Resource does not exist"]
29362936
fn missing_resource_error() {
29372937
#[derive(Resource)]
29382938
pub struct MissingResource;

examples/ecs/error_handling.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ fn main() {
5555

5656
// If we run the app, we'll see the following output at startup:
5757
//
58-
// WARN Encountered an error in system `fallible_systems::failing_system`: "Resource not initialized"
58+
// WARN Encountered an error in system `fallible_systems::failing_system`: Resource not initialized
5959
// ERROR fallible_systems::failing_system failed: Resource not initialized
6060
// INFO captured error: Resource not initialized
6161
app.run();

0 commit comments

Comments
 (0)