Skip to content

Conflicting method names in the same trait hierachy can only ever be called with UFCS syntax #17151

@Kimundi

Description

@Kimundi

During the writing of this issue I reached the conclusion that the current semantic is probably the best solution to this problem already, but I'm opening this issue anyway to encourage discussion or at least getting confirmation from official site that this is the intended behavior.


Right now, you can use the same method name in different traits, even if they exist in a inheritance relation, eg this compiles:

mod foo {
    pub trait Foo {
        fn foo(&self) {}
    }

    pub trait Bar: Foo {
        fn foo(&self) {}
    }

    impl Foo for uint{}
    impl Bar for uint{}
    impl Foo for int{}
    impl Bar for int{}
}

This is currently possible to prevent a fragile base class problem: A library maintainer providing a supertrait should be able to add an new method without breaking downstream code.

However, even if you don't explicitly import Foo or Bar, any place where you try to use the foo() method of Bar you get a "conflicting methods in scope" error, because super traits are automatically brought in scope for method resolution:

In generics:

fn gen<T: foo::Bar>(t: T) {
    t.foo();
}
<anon>:18:5: 18:12 error: multiple applicable methods in scope [E0034]
<anon>:18     t.foo();
              ^~~~~~~
<anon>:18:5: 18:12 note: candidate #1 derives from the bound `foo::Bar`
<anon>:18     t.foo();
              ^~~~~~~
<anon>:18:5: 18:12 note: candidate #2 derives from the bound `foo::Foo`
<anon>:18     t.foo();
              ^~~~~~~

In trait objects:

fn main() {
    let y = vec![box 0u as Box<foo::Bar>, box 0i as Box<foo::Bar>];
    for e in y.iter() {
        e.foo();
    }
}
<anon>:24:9: 24:16 error: multiple applicable methods in scope [E0034]
<anon>:24         e.foo();
                  ^~~~~~~
<anon>:24:9: 24:16 note: candidate #1 derives from the type of the receiver, which is the trait `foo::Bar`
<anon>:24         e.foo();
                  ^~~~~~~
<anon>:24:9: 24:16 note: candidate #2 derives from the type of the receiver, which is the trait `foo::Foo`
<anon>:24         e.foo();
                  ^~~~~~~

Which means in practice you'd run into this problem anyway.

Calling associated functions works though, so you could use UFCS to differentiate the methods:

fn gen<T: foo::Bar>(t: T) {
    foo::Foo::foo(&t);
    foo::Bar::foo(&t);
}

However, a conflict like this still forces a code change decision on the provider of the trait and all downstream users:

  • Changing all code calling the method to the UFCS syntax. (forced for downstream users because else they get conflicting methods errors)
  • Renaming the method and all its uses in the sub trait (Can lead to downstream users accidentally calling the new super trait method instead)

Both options are approximately equally big changes for downstream users, and none of them allows conflicting method names to be used with the original dot syntax, so the question is if this is working as intended, or whether it might be improved upon.

At the very least, it might be a good idea to implement a warn-per-default lint for the conflicting method name in the sub trait definition.

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-type-systemArea: Type systemC-feature-requestCategory: A feature request, i.e: not implemented / a PR.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