NEEDED: Lints to check for useful trait implementations

Background

I recently was reading the book "Rust for Rustaceans" and found a part where it mentioned that it is generally a good idea to implement certain commonly useful traits on your types.

Even if you personally won't use one of those traits' functionality, there is a chance one of your API users will, and the Orphan Rule makes it hard for them to implement those traits on your types after the fact.

Useful traits

In this spirit, I have a list of traits I want to derive for my types (where compatible):

use serde::{Deserialize, Serialize};

#[derive(Clone, Debug, Default, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
struct MyType {
    // ...
}

This means I now have a list of 10 different traits that I want to start implementing more consistently in my projects.

I could just check manually, but that could get laborious and error-prone depending on the volume of code involved. I'd much rather automate the process of checking for this stuff.

Lints

I noticed there are lints to warn me when I am passing-up on opportunities to implement these useful traits, but I've only been able to find such lints for 2 of them: Copy and Debug:

#![warn(missing_copy_implementations)]
#![warn(missing_debug_implementations)]

What about the other useful traits out there? Is there anything I can do to also have lints set for them as well?

Ha, this is a rustc lint, and there was an attempt to remove it.

1 Like

While I understand the motivation behind such a lint for Debug, and partially also for Copy, the notion of useful is very subjective. Some type may contain values, which are Eq or Ord, but for the type itself it does not make sense to implement those due to the respective domain.
So the set of useful trait implementations is highly variable across domains and applications.

2 Likes

I’m pretty confident that you don’t want Copy. This is consistent with Rust’s preference for a “move” instead of “copy” semantic.

Let the user be explicit about having to figure it out with a newtype pattern if need be. This makes explicit when any non-trivial memory allocation is at play.

Keep in mind Copy is a flag/signal trait that requires Clone, but otherwise the two traits are orthogonal.

I oppose this blanket assertion. It depends, whether it makes sense.
I am currently implementing a networking protocol (Zigbee), which uses u8, u16 and the likes to represent bit flags. In order to implement a proper API, I use the eponymous bitflags crate which requires a newtype wrapper for the respective struct, so I mostly end up with something like

#[derive(Clone, Copy, Debug, Eq, PartialEq)
#[repr(transparent)]
struct MyFlags(u8);

bitflags! {
    impl MyFlags: u8 ...
}

for those.

Also note, that I didn't derive Ord* here. u8 is Ord, but ordering sets of bitflags does not make any sense in this context (though it could be useful if you want to use the flags as keys in a BTreeMap, I guess).

1 Like

Sold...

I stand by what i said. In this case we are dealing with low memory consumption

1 Like