-
-
Notifications
You must be signed in to change notification settings - Fork 14.5k
Description
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.