Search code examples
rustmacrostraits

How to ignore generic argument for `#[derive(Debug)]`?


Here's a minimal example representing the type of problem I'm running into.

use core::fmt::Debug;

pub trait Config {
    type A: Debug;
}

#[derive(Debug)]
pub struct Info<T: Config> {
    pub field: T::A,
}

pub struct Conf;

impl Config for Conf {
    type A = i128;
}

fn main() {
    let s = Info::<Conf> {
        field: 123
    };
    dbg!(s);
}

The framework that I'm using (Substrate) uses this concept of a Config trait that aggregates all generic types for a module (pallet).

The problem is that trying to #[derive(Debug)] for a struct that only uses the associated types of the object T implementing Config still requires that T implements Debug itself.

error[E0277]: `Conf` doesn't implement `Debug`
  --> src/main.rs:22:5
   |
22 |     dbg!(s);
   |     ^^^^^^^ `Conf` cannot be formatted using `{:?}`
   |
   = help: the trait `Debug` is not implemented for `Conf`
   = note: add `#[derive(Debug)]` to `Conf` or manually `impl Debug for Conf`

Moreover, I don't have control of the implementation of the Conf object. Regardless, I'm not trying to print anything about the Conf object itself.

Is there a way to make #[derive(Debug)] for Info ignore T?


Solution

  • Unfortunately, not as of today. You have to impl Debug manually:

    impl<T: Config> fmt::Debug for Info<T> {
        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
            f.debug_struct("Info").field("field", &self.field).finish()
        }
    }
    

    There is an intent to make it possible to create what is called "perfect derive": derive bounds based on what needed and not the generic parameters. See for example this lang team design meeting proposal. But for now there is nothing.