rust icon indicating copy to clipboard operation
rust copied to clipboard

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

Open aturon opened this issue 7 years ago • 14 comments

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()
   |     ^^^^^^^^^^^^^^^^^

aturon avatar Nov 04 '16 21:11 aturon

cc @nikomatsakis (and @alexcrichton, since this will prevent us from using this trick in futures for now).

aturon avatar Nov 04 '16 21:11 aturon

noooooooooooooo

alexcrichton avatar Nov 04 '16 21:11 alexcrichton

@aturon huh, that's annoying!

nikomatsakis avatar Nov 08 '16 20:11 nikomatsakis

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

eddyb avatar Nov 09 '16 23:11 eddyb

@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>()).

nikomatsakis avatar Nov 11 '16 19:11 nikomatsakis

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

eddyb avatar Nov 11 '16 20:11 eddyb

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

nikomatsakis avatar Nov 11 '16 20:11 nikomatsakis

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!

alexcrichton avatar Nov 11 '16 21:11 alexcrichton

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

eddyb avatar Nov 11 '16 21:11 eddyb

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

alexcrichton avatar Nov 11 '16 21:11 alexcrichton

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

eddyb avatar Nov 11 '16 21:11 eddyb

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`

malbarbo avatar Nov 14 '16 16:11 malbarbo

@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();
}

eddyb avatar Nov 14 '16 17:11 eddyb

triage: still reproduces

adjusted repro:

trait Assoc {
    type Ty;
}

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

fn main() {
    <dyn Assoc>::non_method()
}

Spoonbender avatar Oct 17 '22 10:10 Spoonbender

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`

estebank avatar Feb 07 '24 00:02 estebank