Standard library types and #[derive(Debug)]

Do I understand correctly that if any non-local type in a struct does not have a Debug implementation, there is no way to use #[derive(Debug)] and I have to write every implementation which references it by hand?

use std::sync::atomic::AtomicUsize;

#[derive(Debug)]
pub struct Source {
  counter: AtomicUsize,
  // maybe lots more which do have Debug impls
}
src/suffuse.rs:74:3: 74:23 error: the trait `core::fmt::Debug` is not implemented for the type `core::atomic::AtomicUsize` [E0277]
src/suffuse.rs:74   counter: AtomicUsize
                    ^~~~~~~~~~~~~~~~~~~~

And of course I'm not allowed to fill the gap:

src/suffuse.rs:72:1: 76:2 error: the impl does not reference any types defined in this crate; only traits defined in the current crate can be implemented for arbitrary types [E0117]
src/suffuse.rs:72 impl Debug for AtomicUsize {

If this accurately represents the situation, it would be nice if some tooling would ensure that all standard library types have a Debug implementation, because it's pretty aggravating to be blocked out of a derived implementation by the standard lib.

(I realize I can work around it by newtyping, but it's boilerplate for writers and obfuscation for readers.)

Yes, you can not use derive unless you're defining something. And, as you've seen, you can't add it by hand either.

Basically everything should have a Debug impl, so this just seems like an oversight.

Iterators and iterator adaptors in libstd don't have Debug impls, I wonder if they should have. Maybe they could have some kind of opaque or simplified debug output.

You could manually implement Debug for your struct:

impl Debug for Source {
    fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
        fmt.debug_struct("Source")
           .field("counter", &self.counter.load(Ordering::Relaxed))
           .finish()
    }
}

playpen

Yeah its not as pretty, but it works! Perhaps Debug is not implemented for AtomicUsize because to get the value, you need to supply an ordering, and we did not want to force a specific ordering for the Debug output?

1 Like

That's what I meant by "I have to write every implementation which references it by hand."

If there's no tooling which verifies everything has a Debug implementation, then there's no call to invent reasoning why this particular omission was somehow intentional. Anything which requires annotating every public type and which isn't machine-verified is going to be full of holes. It's an immutable property of humans.

There's actually a lint to check if Debug is on all your types. It's just 'allow' by default. It could perhaps be made a warning for libstd, therefore catching these cases.

2 Likes

fmt documentation states:

fmt::Debug implementations should be implemented for all public types.

Guess it is time for a PR.

Did anyone filed a PR yet? I tried searching for it found nothing appeared.

If it's all implemented in one big PR, then we can measure and see what effect it has on cycle time. I sort of dread that it's going to make rust even slower to compile. Maybe there is something we can do to limit inlining in derived debug impls.