Given a type in a library whose internal representation is not exposed. I am always wondering should I "expose" the internal representation via deriving Debug or should I have a custom Debug implementation.
I have looked at some examples in the std and I found two interesting cases that seem to contradict each other:
std::time::Duration is internally represented by secs and nanos integers, but Debug has a custom implementation, so for example
So there it exposes the sec and nanosec integers in some sense. So why have a human readable representation for Duration but a quite similar representation than the internals of Duration for SystemTime? Is there a general rule of thumb when to derive Debug and when to try to make it human readable by a custom implementation?
Derive unless you have a specific reason not to. Almost everything derives and thus leaks internal details in some way, but it's not considered reasonable for others to rely on those details. (And the details are already observable for anything you have the source for, e.g. any crates.io crate.)
Personally the specific reasons I have had are
avoiding Debug bounds on generics
derive doesn't work (e.g. because a field type didn't apply the previous bullet)
sensible error type output for end users due to Termination for Result using Debug (alernatively use anyhow or friends)
Interesting insight. Staying in the domain of time, I looked at a few third-party crates and seem like both I looked at (chrono and jiff) seem to have custom Debug implementations to have human-readable representations, for example: NaiveDateTime in chrono - Rust and DateTime in jiff::civil - Rust.
So in your opinion are those two crates doing it wrong, or would you say this makes sense for them?
I excluded several fields in the implementation to prevent the case I mentioned above. I think it's only a case when a custom implementation is critical.
Another reason for a custom Debug is when a type holds a large or shared structure. E.g., a cache or, continuing the time theme, a time zone database. Displaying a list of such things can be needlessly verbose and repetitive, a custom Debug can make the values clearer by not showing useless internal detail. A tz name is much more useful than a table of offsets - I did not check/have no idea whether this is the reason chrono or jiff have custom Debug.
Seems to me that the difference is that SystemTime is dealing with something defined in the Posix standard from 1996. POSIX.1-1996. Which is unlikely to ever change. So it is reasonable for Debug to reflect that standard.