Pre-Pre-RFC: Ignoring fields in derive(Debug, PartialEq)


#1

Currently deriving the “standard” traits such as Debug and PartialEq for structs requires that all the fields implement the trait in question. For some traits this is a hard requirement; one could hardly imagining .clone()ing a struct whose all fields are not cloneable. However, there are some cases where more flexibility is warranted.

The main motivator for more flexible deriving is Debug: there often are cases where you want to inspect the state of some struct using debug printing or logging, only to find that you can’t derive Debug for it because one of the fields don’t implement it, and have to painstakingly hand-roll the implementation for a simple peek.

Similar, although maybe not as pressing usecases come forth when writing tests. Very often you want to assert_eq! the result value with some expected value. In my experience, sometimes you just want to build a “similar enough” object to tests against some returned object state – if the same object contains some opaque fields, you don’t neccessarily want to test them, but the other ones. This requires the object to implement PartialEq, which it may not do because of the “opaque” fields.

I think it would be beneficial at least for these two trait derivations to have Serde-like field attributes. For those unfamiliar with Serde, it allows easiy customisation of derives of Serialize and Deserialize using attributes on fields:

#[derive(Serialize, Deserialize)]
struct Point {
    x: i32,
    y: i32,
    #[serde(skip)]
    debug_data: SomeData,
}

I am proposing that the derives of Debug and PartialEq would have similar supported attributes such as #[debug(ignore)] or #[partial_eq(ignore)]. (The actual syntax is bikesheddable.) This would help with some small but irritating papercuts that manifest themselves especially when doing debugging, testing and prototyping work, where quickness and ease are of paramount importance.

I’d like to write an RFC around this, but I’d like to gather some feedback first. Is there any show-stoppers for this? I think there is certainly demand.


#2

It seems reasonable. But I guess there’s a bunch of question to think about. For example, in case of debug, should it completely ignore the field, or have something like debug_data: /* Opaque stuff */ in the output? Do you also want to support custom „hooks“ (eg. function that compares/prints just that field)? If yes, should this be extended for more traits? ‒ with this it would be possible to implement the clone as well.


#3

I like the idea but this can be done with a proc macro. There is one for Debug:

debug_stub_derive

And implementing it for PartialEq should also be possible.


#4

There is this crate: (actually one of the earliest proc macro creates, if I recall)

https://crates.io/crates/derivative

#[derive(Derivative)]
#[derivative(PartialEq, Hash)]
pub struct Version {
    pub major: u64,
    pub minor: u64,
    pub patch: u64,
    pub pre: Vec<Identifier>,
    #[derivative(PartialEq="ignore")]
    #[derivative(Hash="ignore")]
    pub build: Vec<Identifier>,
}

#5

Indeed, it is worth doing a thorough survey what Serde (which is after all a very polished library) does and what do the other crates mentioned in this thread do. I wonder if one should tread conservatively and only address the actual pain points or if a larger design should considered right away. Both approaches have their merits. It might be good to explore the space and if there’s contention about what to include, settle with a subset of features.


#6

@willi_kappler @ExpHP Thanks for pointing these out. Indeed, this can be done with a proc macro, and so can be done the current standard library derives too. (Of course, this didn’t use to be the case before Rust 1.15.) It’s good to know that this functionality has demand, since that’s usually the first thing to ask: is there already a 3rd party crate and do people use it?

Now, the next question to be asked, is there enough demand for inclusion in standard library? The stdlib already has a stripped version of what Derivative offers, but eye-balling trough Derivative’s documentation, there might be more than what is expected of stdlib. As Rust strives for a small standard library, the question then becomes: is the current level of functionality in balance, or is there something “general-use” enough that pulls it’s weight.

From my experience, ignoring fields with Debug (or even better, setting how they should be shown, as that can be considered strictly more general functionality) is common enough to pull its weight.

Anyway, thanks for the replies!