rust-lang / rust-lang/rust-clippy

Warn against having multiple `impl` blocks for the same struct and with the same bounds(?)

Open
#8,714 2 comments 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

A-lint
Dominant language
Rust
Stars
13.5k
Forks
2.2k
Avg merge
2d 10h
Merged PRs (30d)
32

Description

What it does

I'm not a weathered crustacean, but I got bitten the other day because I mistakenly created the same impl block twice in the same file but with different functions, and I didn't realise it for ages but kept on getting strange inconsistencies because of it.

So my file looked something like

struct Foo<'a> {
    i: &'a mut i32,
}

// lots of code here

impl Foo<'_> {
    fn bar(&mut self) {
        *self.i += 1;
    }
}

// lots of code here

impl Foo<'_> {
    fn baz(&mut self) {
        *self.i += 2;
    }
}

// lots of code here

fn main() {
    println!("Hello world");
    // And more code here
}

I'm not sure if this is worth linting for to warn the user that this probably isn't a thing they intended? It's probably more common in larger projects with new people joining and not knowing if something's been impl-ed yet.

If someone more knowledgeable knows of a situation in which you would use two impl blocks with identical headers, I'd be interested to hear it as well.

Lint Name

duplicate-impl-headers

Category

suspicious, style, complexity

Advantage
  • Remove confusion introduced in bigger projects
  • Guide the user away from creating an impl block for every single function they want for their struct
  • Make it clear that different impl blocks are actually different in what they allow or what they can contain
Drawbacks
  • Maybe a bit picky
  • I don't think it would crop up very often
  • There might be a legitimate use case for multiple impls
  • I'm not sure if impls which are duplicated but in different files should be flagged or not
Example

From the description:

struct Foo<'a> {
    i: &'a mut i32,
    // j: &'b mut i32,
}

// lots of code here

impl Foo<'_> {
// --------- note bar in this impl ---------
    fn bar(&mut self) {
        *self.i += 1;
    }
}

// lots of code here

impl Foo<'_> {
// --------- note baz in this impl ---------
    fn baz(&mut self) {
        *self.i += 2;
    }
}

// lots of code here

fn main() {
    println!("Hello world");
    // And more code here
}

Could be written as:

struct Foo<'a> {
    i: &'a mut i32,
}

// lots of code here

// --------- note bar and baz now in one impl ---------
impl Foo<'_> {
    fn bar(&mut self) {
        *self.i += 1;
    }
    fn baz(&mut self) {
        *self.i += 2;
    }
}

// lots of code here

fn main() {
    println!("Hello world");
    // And more code here
}

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the issue's duplicate-impl examples and the proposed lint name, then investigate whether identical impl headers have legitimate uses and whether separate files should be included. Done means the lint's scope, exceptions, category, and expected diagnostics are agreed, with implementation and tests identified.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
tooling
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.