Skip to content

Lint against inherent methods on types implementing Receiver and Deref #151583

@BennoLossin

Description

@BennoLossin

When inherent methods are added to types that implement Receiver or Deref today, they take precedence over methods with the same name on the type they receive for or deref to. This means that it becomes impossible to call those with the method calling syntax, resulting in poorer ergonomics for the type that implements Receiver or Deref. For example:

pub struct MyBox<T>(Box<T>);

impl<T> Deref for MyBox<T> {
    type Target = T;

    fn deref(&self) -> &T {
        &*self.0
    }
}

impl<T> MyBox<T> {
    pub fn method(&self) -> u32 {
        42
    }
}

// ---

struct Struct;

impl Struct {
    fn method(&self) -> i32 {
        -42
    }
}

fn main() {
    let s = MyBox(Box::new(Struct));
    
    let x: i32 = s.method(); //~ ERROR: mismatched types
}

Since Receiver exists purely for these ergonomic reasons -- allowing methods to be called using the method syntax with custom receiver types -- this goes directly against the spirit of that trait. In the standard library it is already standard practice to not add inherent methods to types like Box and Arc and instead make them associated functions.

For this reason, there should be a lint that triggers on inherent methods on types that implement Receiver or Deref.

In the example above, the lint could look like this:

warning: inherent methods on types implementing `Deref` shadow methods on the target
  --> src/main.rs:14:4
   |
5  | impl<T> Deref for MyBox<T> {
   | ---------------------------- `Deref` implemented for `MyBox` here
...
13 | impl<T> MyBox<T> {
   | ------------------ inherent method in this `impl` block
...
14 |     pub fn method(&self) -> u32 {
   |     ^^^           ^^^^^ publicly accessible inherent method declared here
help: consider making `method` an associated function:
   |
14 |     pub fn method(this: &Self) -> u32 {
   |                   +++++++++++

The lint should only fire on methods that are pub (or the maximal publicity of the type, so a pub(crate) type would also trigger on pub(crate) functions).

Another important consideration is that Deref is often used for the subtyping pattern or for dereferencing to a "morally contained type". In this case, the target type is not a generic, but a concrete type or a rigid type containing a generic (for example [T]). For this reason the lint for Deref should not trigger in those cases. The rationale is that since one knows the concrete type, avoiding name collisions is much easier. It also avoids false positives, since in the subtyping pattern, both types want to have inherent methods. In particular this will ensure that the Deref implementation for Vec will not trigger the lint.

Zulip discussion

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-lintsArea: Lints (warnings about flaws in source code) such as unused_mut.C-feature-requestCategory: A feature request, i.e: not implemented / a PR.I-lang-nominatedNominated for discussion during a lang team meeting.I-lang-radarItems that are on lang's radar and will need eventual work or consideration.P-lang-drag-3Lang team prioritization drag level 3.https://rust-lang.zulipchat.com/#narrow/channel/410516-t-lang.T-langRelevant to the language team

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions