Please let me know what you're thinking about it. Also, should I keep the empty sections on real RFC?
Summary
Adds struct MaybeDebug<T>(&T);
. which implements Debug
trait regardless whether the T
implements it.
Motivation
Debug
trait is used for the programmer-facing, debugging context formatting. Most public types implements this trait to enable casual debugging. But since it's a regular trait not something built into the language, you need to explicitly adds trait bound to use it in the generic context. This can be painful with deeply nested generic code, and sometimes it affects the public API of the crate which is not needed for the functionality.
Since it's testing/casual debugging, we usually know from the outside that the actual type passed into have the Debug
impl. What does it prints if it's not is not much important as debug logging doesn't affect the actual functionality. For casual debugging in such context we need some way to print its Debug
format if it has one, print some dummy text otherwise.
Guide-level explanation
Adds struct MaybeDebug<'a, T>(pub &'a T);
type into the core::fmt
module. This type implements Debug
trait regardless whether the T
does it. Its implementation transparently forwards to the T
's implementation if it exist. If not, It prints the core::any::type_name::<T>()
instead to point you what to fix if you want more information.
debug!("what happens here: {:?}", MaybeDebug(&client.conn));
Reference-level explanation
Adds struct MaybeDebug<'a, T>(pub &'a T);
type into the core::fmt
module. It has one base implementation of the trait Debug
, and a specialized implementation of the same trait for the T: Debug
case. The base implementation SHOULD provide enough information to casually identify what the concrete type T
is. The formatted output of the specialized implementation SHOULD be identical with the output from the T
's Debug
implementation. In both cases, the exact output format is not considered stable.
Drawbacks
It adds up additional API surface to the stdlib.
Rationale and alternatives
It can be implemented as a 3rd party crate. But since it uses the specialization feature, users with the stable compiler can't use it. And it doesn't seems like the specialization feature can be stabilized soon.
The field T
can be stored by-value. This would simplify the type signature and would be more idiomatic if T: Copy
. Non-Copy types can still be used as references due to the Debug
implementation of the &T
though it affect the output of the type_name::<T>()
.
Instead of the wrapper type, we can add another extension trait with method returning Option<&dyn Debug>
with specialized wildcard implementation. This allows more fine grained control.