Skip to content

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

@beyarkay

Description

@beyarkay

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
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-lintArea: New lints

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions