Skip to content

Trait impls from where clauses (ParamEnv) take precedence over freestanding trait impls #24066

Open

Description

@hanna-kruppe

MVCE:

trait Trait<T> {
    fn call_me(&self, x: T) {}
}
impl<T> Trait<u32> for T {}
impl<T> Trait<i32> for T {}
fn bug<T: Trait<U>, U>(x: T) {
    x.call_me(1u32);
    //~^ ERROR mismatched types
}

This is due to the fact that we prefer where-bounds over impls, even if the where-bounds have unnecessary inference constraints.


I stumbled upon this trying to write a function that generically takes a range (or something else with which str can be indexed), and slices a str with that range. I got that to work, this bug comes into play when the str is produced by slicing another string with a RangeFrom or another concrete type (in the same function).

Here's a simpler (no lifetimes, no associated types) and self-contained example (playpen):

trait Frobnicate<I> {
    fn frob(&self, i: I) -> Self;
}

struct Thing;

struct IA;
struct IB;

impl Frobnicate<IA> for Thing {
    fn frob(&self, _i: IA) -> Thing {
        Thing
    }
}

impl Frobnicate<IB> for Thing {
    fn frob(&self, _i: IB) -> Thing {
        Thing
    }
}

fn delegate_frob<I>(t: Thing, i: I) -> Thing
where
    Thing: Frobnicate<I>,
{
    let t2: Thing = t.frob(IA);
    t2.frob(i)
}

This seems innocent enough. There's a impl Frobnicate<IA> for Thing, so the .frob(IA) call should work, and it's known to return Thing again, so via the where clause the .frob(i) call is good as well. However, I get this error message:

error[E0308]: mismatched types
  --> src/main.rs:26:28
   |
22 | fn delegate_frob<I>(t: Thing, i: I) -> Thing
   |                  - expected this type parameter
...
26 |     let t2: Thing = t.frob(IA);
   |                       ---- ^^ expected type parameter `I`, found `IA`
   |                       |
   |                       arguments to this method are incorrect
   |
   = note: expected type parameter `I`
                      found struct `IA`
note: method defined here
  --> src/main.rs:2:8
   |
2  |     fn frob(&self, i: I) -> Self;
   |        ^^^^        -

It appears that the where clause makes the compiler forget that there are other impls.
Adding a Thing : Frobnicate<IA> bound only makes the compiler (rightly) complain that that's not a bound at all, since it doesn't involve any type parameters.
UFCS makes it compile, though:

    let t2: Thing = <Thing as Frobnicate<IA>>::frob(&t, IA);

Edit: Besides playpen, I can also reproduce this locally:

rustc 1.0.0-beta (9854143cb 2015-04-02) (built 2015-04-02)
binary: rustc
commit-hash: 9854143cba679834bc4ef932858cd5303f015a0e
commit-date: 2015-04-02
build-date: 2015-04-02
host: x86_64-pc-windows-gnu
release: 1.0.0-beta

But I already noticed it a couple of weeks ago, so it can't be a recent regression.

Activity

  1. bluss commented on May 6, 2016

    @bluss
    Contributor

    @nikomatsakis Is this not a bug? It seems to be the feature that was discussed in #33108 of lifting trait methods to inherent methods.

  2. nikomatsakis commented on May 9, 2016

    @nikomatsakis
    Contributor

    I would say this is... a grey area. Perhaps a known shortcoming would be the best description. The trait selection algorithm definitely prefers where clauses to impls when there is inference involved -- this often is crucial to making progress, but here creates a problem. You could probably workaround it by adding some (redundant) where-clauses like where Thing : Frobnicate<IA>, but that shouldn't be necessary. I'm not sure though of the best way to improve the trait selection algorithm to circumvent this problem.

  3. rocallahan commented on Dec 6, 2016

    @rocallahan

    Here's a simple example of what I think is the same bug:

    fn f<V>(_: V) -> String where String: From<V> { String::from("hello") }
    

    This is definitely a problem.

  4. Mark-Simulacrum commented on May 4, 2017

    @Mark-Simulacrum
    Member

    The example in the issue description now compiles. Closing.

  5. Mark-Simulacrum commented on May 4, 2017

    @Mark-Simulacrum
    Member

    Not again, I tested in the wrong directory. Re-opening....

  6. dhardy commented on Feb 8, 2018

    @dhardy
    Contributor

    "Me too" — play example — forum.

  7. added a commit that references this issue on Jun 9, 2018
  8. added
    T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.
    on Jun 9, 2019
  9. Pzixel commented on Oct 24, 2019

    @Pzixel

    Small example to illustrate the problem (I do velieve it caused by the same bug):

    fn f<V>(_: V) -> String
    where
        String: From<V>,
    {
        From::from("Hello")   // works
        String::from("Hello") // doesn't work
    }

    playground

  10. Globidev commented on Jul 13, 2021

    @Globidev

    One thing I noted while investigating a related issue also applies to @Pzixel's example above, where adding another trait bound can fix the problem in strange ways. The type parameter of the generic trait in the extra bound can be another generic type parameter or an HRTB with a concrete type, but not another concrete type.
    Also in this specific case with String and From it cannot even be the expected concrete type for some reason. And I can't reproduce the behavior with either a local trait or a local type

    Here is more (confusing) food for thought as of 1.55.0-nightly (2021-07-12 955b9c0d4cd9176b53f5) :
    playground link

    fn ok_with_another_bound_generic<V, W>() -> String
    where
        String: From<V> + From<W>,
    {
        String::from("✅")
    }
    
    fn nok_with_another_bound_concrete<V>() -> String
    where
        String: From<V> + From<char>,
    {
        String::from("❌")
    }
    
    fn ok_with_another_bound_concrete_hrtb<V>() -> String
    where
        String: From<V> + for<'a> From<&'a String>,
    {
        String::from("✅")
    }
    
    fn nok_with_another_bound_concrete_expected_type<V>() -> String
    where
        String: From<V> + From<&'static str>,
    {
        String::from("❌")
    }
    
    fn ok_with_another_bound_concrete_expected_type_local_trait<V>() -> String
    where
        String: MyFrom<V> + MyFrom<&'static str>,
    {
        String::my_from("✅")
    }
    
    fn ok_with_another_bound_concrete_expected_type_local_type<V>() -> MyString
    where
        MyString: From<V> + From<&'static str>,
    {
        MyString::from("✅")
    }
    
    trait MyFrom<T> { fn my_from(_: T) -> Self; }
    impl<T> MyFrom<T> for T { fn my_from(x: T) -> Self { x } }
    impl MyFrom<&str> for String { fn my_from(c: &str) -> Self { c.into() } }
    
    struct MyString;
    impl From<&str> for MyString { fn from(_: &str) -> Self { MyString } }
  11. added
    T-typesRelevant to the types team, which will review and decide on the PR/issue.
    on Jan 23, 2024
  12. changed the title [-]Two impls: "Expected (type parameter), got (struct that also has impl)"[/-] [+]Trait impls from where clause (`ParamEnv`) take precedence over freestanding trait impls[/+] on Jan 24, 2024
  13. changed the title [-]Trait impls from where clause (`ParamEnv`) take precedence over freestanding trait impls[/-] [+]Trait impls from where clauses (`ParamEnv`) take precedence over freestanding trait impls[/+] on Jan 26, 2024
  14. tyilo commented on Jul 17, 2024

    @tyilo
    Contributor

    I think I ran into the same problem with some code that I reduced to:

    Playground

    use std::ops::Mul;
    
    #[derive(Debug)]
    struct UsizeWrapper(usize);
    
    impl Mul<UsizeWrapper> for usize {
        type Output = UsizeWrapper;
    
        fn mul(self, rhs: UsizeWrapper) -> Self::Output {
            UsizeWrapper(self * rhs.0)
        }
    }
    
    fn mul_6<T>(v: T) -> T
    where
        usize: Mul<T, Output = T>,
    {
        // Doesn't work:
        2usize * 3usize * v
        // Works:
        //6usize * v
    }
    
    fn main() {
        println!("{:?}", mul_6(UsizeWrapper(10usize)));
    }

    Error:

    error[E0308]: mismatched types
      --> src/main.rs:19:14
       |
    14 | fn mul_6<T>(v: T) -> T
       |          - expected this type parameter
    ...
    19 |     2usize * 3usize * v
       |              ^^^^^^ expected type parameter `T`, found `usize`
       |
       = note: expected type parameter `T`
                            found type `usize`
    
    error[E0369]: cannot multiply `T` by `T`
      --> src/main.rs:19:21
       |
    19 |     2usize * 3usize * v
       |     --------------- ^ - T
       |     |
       |     T
       |
    help: consider further restricting type parameter `T`
       |
    16 |     usize: Mul<T, Output = T>, T: std::ops::Mul<Output = T>
       |                              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    Some errors have detailed explanations: E0308, E0369.
    For more information about an error, try `rustc --explain E0308`.
    
  15. marked Type inference problem #95358 as a duplicate of this issue on Aug 1, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-trait-systemArea: Trait systemC-bugCategory: This is a bug.T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.T-typesRelevant to the types team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions