This is a Pre-RFC for a new attribute which could be used to customise the behaviour of derive(Debug)
. I'm posting here mostly to get feedback on the usefulness/lack thereof of the proposed variants
- Feature Name: (
derive_debug_attributes
) - RFC PR: TODO
- Rust Issue: TODO
Summary
Introduce a #[debug]
attribute, used to control various aspects of the Debug
derive macro.
#[derive(Debug)]
#[debug(transparent)]
struct NewType(Vec<u8>);
// dbg!(newtype) => "[1, 2, 3, 4]"
#[derive(Debug)]
struct SkipField<'a> {
#[debug(skip)]
context: &'a VeryLargeContextType,
field: u8,
}
// dbg!(skipfield) => SkipField { field: 5 }
#[derive(Debug)]
struct FnStruct<F: Fn() -> u8> {
#[debug(type)]
f: F,
}
// dbg!(fnstruct) => FnStruct { f: crate::main::{{closure}} }
#[derive(Debug)]
struct Rename {
#[debug(rename = "bar")]
foo: u8,
}
// dbg!(rename) => Rename { bar: 0 }
Motivation
Allowing tweaks to the behaviour of derive(Debug)
without requiring manual implementation or proc-macros reimplementing the bulk of the current behaviour.
Guide-level explanation
The #[default]
attribute can be used to customize the behavior of the #[derive(Debug)]
macro.
#[debug(transparent)]
This attribute may only be applied to a single-field struct, and simply uses that field's Debug
implementation. This can be used, for example, to introduce a newtype that is displayed the same as the inner type.
#[debug(skip)]
This attribute, when applied to a field, skips it entirely when debug formatting.
#[debug(type)]
This attribute, when applied to a field, uses the std::any::type_name
of the field rather than its value. This can be useful for structs containing a closure or function pointer.
#[debug(rename = "NAME")]
This attribute, when applied to a named field, simply displays the field using the provided NAME
.
Reference-level explanation
A builtin-in attribute #[debug]
is provided to the compiler, and may only be used on a type with #[derive(Debug)]
applied.
TODO
Drawbacks
- Adds complexity to the use of the Debug derive macro.
- For
skip
andtype
to be more useful, types only used in fields marked as such should be exempt from the automatic derives placed on any generic types in the struct, and I'm uncertain if thedebug
attribute is the place to solve that (Perfect Derive)
Rationale and alternatives
- Current approaches to solving issues with the automatic Debug derive are to implement by hand and use third-party derive macro crates
- Implementing by hand leads to verbose, overlong code
- Using a third party crate requires a lot of complexity, e.g. pulling in
syn
simply to create an implementation which does less - Both approaches do not benefit from the highly tweaked and optimised current derive code
Prior art
The derivative crate is the main inspiration for this, which provides custom configurable versions of several built-in derives, but has not been updated in some time.
The attribute naming is based on the #[default]
attribute, used by the Default
derive macro for enums.
Future possibilities
More variants of the #[debug]
attribute could be introduced in order to allow further tweaking.
Other derive macros could receive similar attributes - being able to skip over a field would be applicable to Eq
, Ord
, Hash
etc.