rust
rust copied to clipboard
Inherent static methods on traits do not play well with associated types
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()
| ^^^^^^^^^^^^^^^^^
cc @nikomatsakis (and @alexcrichton, since this will prevent us from using this trick in futures for now).
noooooooooooooo
@aturon huh, that's annoying!
@nikomatsakis Should we infer unspecified associated types in expressions, just like type parameters?
@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>()
).
Presumably it'd be inferrable from the signature? @alexcrichton can you give more details?
@eddyb I was just going on the example, where T
was not needed; but yeah perhaps in real cases, T
would be used.
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!
Yeah that sounds like only needs to do inference and it'd work.
Er sorry the -> Self
would actually be replaced with -> impl Future<Item=T, Error=E>
, not sure if that changes things
@alexcrichton Both -> impl Future<Item=T, Error=E>
and -> Box<Self>
would work the same.
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 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();
}
triage: still reproduces
adjusted repro:
trait Assoc {
type Ty;
}
impl<T> dyn Assoc<Ty = T> {
fn non_method() {}
}
fn main() {
<dyn Assoc>::non_method()
}
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`