Inherent static methods on traits do not play well with associated types

19fe296
Opened by Aaron Turon at 2024-02-07 00:59:42

It's a nice trick that you can provide static methods on bare traits, but sadly this doesn't quite work with associated types:

trait Assoc {
    type Ty;
}

impl<T> Assoc<Ty = T> {
    fn non_method() {}
}

fn main() {
    Assoc::non_method()
}

Error:

error[E0191]: the value of the associated type `Ty` (from the trait `Assoc`) must be specified
  --> <anon>:10:5
   |
10 |     Assoc::non_method()
   |     ^^^^^^^^^^^^^^^^^ missing associated type `Ty` value

error: no associated item named `non_method` found for type `Assoc` in the current scope
  --> <anon>:10:5
   |
10 |     Assoc::non_method()
   |     ^^^^^^^^^^^^^^^^^
  1. cc @nikomatsakis (and @alexcrichton, since this will prevent us from using this trick in futures for now).

    Aaron Turon at 2016-11-04 21:41:02

  2. noooooooooooooo

    Alex Crichton at 2016-11-04 21:41:54

  3. @aturon huh, that's annoying!

    Niko Matsakis at 2016-11-08 20:48:33

  4. @nikomatsakis Should we infer unspecified associated types in expressions, just like type parameters?

    Eduard-Mihai Burtescu at 2016-11-09 23:08:27

  5. @eddyb

    Should we infer unspecified associated types in expressions, just like type parameters?

    Hmm, probably, yes. However, that alone wouldn't be good enough. We'd also need the "GC" strategy that we were talking about in the context of HKT, where unbound type variables are permitted if we can tell they don't matter at all. =)

    Unfortunately, I'm not sure how we would be supposed to know that here: the type of T may very well matter, even to a "static" method. (It could e.g. call sizeof::<T>()).

    Niko Matsakis at 2016-11-11 19:57:49

  6. Presumably it'd be inferrable from the signature? @alexcrichton can you give more details?

    Eduard-Mihai Burtescu at 2016-11-11 20:01:48

  7. @eddyb I was just going on the example, where T was not needed; but yeah perhaps in real cases, T would be used.

    Niko Matsakis at 2016-11-11 20:06:22

  8. The context here is that in the futures crate we've got:

    trait Future {
        type Item;
        type Error;
        // ...
    }
    

    We've also got a number of "bare futures" such as:

    fn finished<T, E>(t: T) -> impl Future<Item=T, Error=E>
    fn failed<T, E>(e: E) -> impl Future<Item=T, Error=E>
    fn done<T, E>(e: Result<T, E>) -> impl Future<Item=T, Error=E>
    

    These "base constructors" are currently free functions, so they need to be imported somehow. Normally, though, the Future trait is in scope, so we'd love to be able to do this instead:

    Future::finished(e); // returns `impl Future<Item=T, Error=E>
    Future::failed(e);
    // ...
    

    That would be empowered through something like @aturon mentioned above:

    impl<T, E> Future<Item=T, Error=E> {
        pub fn finished(e: E) -> Self { ... }
        pub fn failed(e: E) -> Self { ... }
        pub fn done(e: E) -> Self { ... }
    }
    

    Not sure whether that helps? That's what we're thinking though!

    Alex Crichton at 2016-11-11 21:37:44

  9. Yeah that sounds like only needs to do inference and it'd work.

    Eduard-Mihai Burtescu at 2016-11-11 21:43:29

  10. Er sorry the -> Self would actually be replaced with -> impl Future<Item=T, Error=E>, not sure if that changes things

    Alex Crichton at 2016-11-11 21:47:02

  11. @alexcrichton Both -> impl Future<Item=T, Error=E> and -> Box<Self> would work the same.

    Eduard-Mihai Burtescu at 2016-11-11 21:49:08

  12. In some cases it is not possible to call a static method defined in trait even if the trait does not have an associated type:

    pub trait X {
        fn f() -> u32 {
            10
        }
    }
    
    fn main() {
        let a = X::f();
    }
    
    error[E0283]: type annotations required: cannot resolve `_: X`
     --> <anon>:8:13
      |
    8 |     let a = X::f();
      |             ^^^^
      |
      = note: required by `X::f`
    

    Marco A L Barbosa at 2016-11-14 16:45:34

  13. @malbarbo That's a method that every implementer of X can customize, if you don't want that:

    pub trait X {}
    
    impl X {
        fn f() -> u32 {
            10
        }
    }
    
    fn main() {
        let a = X::f();
    }
    

    Eduard-Mihai Burtescu at 2016-11-14 17:11:48

  14. triage: still reproduces

    adjusted repro:

    trait Assoc {
        type Ty;
    }
    
    impl<T> dyn Assoc<Ty = T> {
        fn non_method() {}
    }
    
    fn main() {
        <dyn Assoc>::non_method()
    }
    

    Maayan Hanin at 2022-10-17 10:15:12

  15. Current output:

    error[E0191]: the value of the associated type `Ty` in `Assoc` must be specified
      --> src/main.rs:10:10
       |
    2  |     type Ty;
       |     ------- `Ty` defined here
    ...
    10 |     <dyn Assoc>::non_method()
       |          ^^^^^ help: specify the associated type: `Assoc<Ty = Type>`
    
    error[E0599]: no function or associated item named `non_method` found for trait `Assoc`
      --> src/main.rs:10:18
       |
    10 |     <dyn Assoc>::non_method()
       |                  ^^^^^^^^^^ function or associated item not found in `Assoc`
    

    Esteban Kuber at 2024-02-07 00:59:42