4

I'm experimenting with impls of my own traits for closures; my trait has an associated type for the closure's output.

This works fine, with the associated type in Fn's return position:

trait MyFn<Input> {
    type State;

    fn call(&self, state: &mut Self::State, input: Input);
}

impl<Input, State, F> MyFn<Input> for F where F: Fn(Input) -> State {
    type State = State;

    fn call(&self, state: &mut State, input: Input) {
        *state = self(input);
    }
}

But I can't get the same associated type to work in the Fn's parameter position:

impl<Input, State, F> MyFn<Input> for F where F: Fn(&mut State, Input) {
    type State = State;

    fn call(&self, state: &mut State, input: Input) {
        self(state, input)
    }
}

https://play.rust-lang.org/?gist=dfbb1a258775d1ac2f92235cc2610cc7&version=stable&mode=debug&edition=2015

error[E0207]: the type parameter `State` is not constrained by the impl trait, self type, or predicates
  --> src/lib.rs:15:13
   |
15 | impl<Input, State, F> MyFn<Input> for F where F: Fn(&mut State, Input) {
   |             ^^^^^ unconstrained type parameter

Surely the type parameter State is constrained by the where predicate?

Tim Robinson
  • 53,480
  • 10
  • 121
  • 138
  • 2
    Separately from your question, Rust will not allow both of these blanket impls to exist. Even if you know that there is no type that qualifies for both impls, Rust doesn't know that such a type can't exist. So, once you solve this error, you'll have that one, which you won't be able to solve. – Peter Hall Sep 20 '18 at 15:22
  • I didn't exactly want these two impls, but I did want something like `F: Fn(Input) -> State` and `for<'a> F: Fn(&'a [Input]) -> Vec`, which falls into the same trap. – Tim Robinson Sep 20 '18 at 15:28
  • 1
    This reminds me of [Specify `Fn` trait bound on struct definition without fixing one of the `Fn` parameters](https://stackoverflow.com/questions/50671177/specify-fn-trait-bound-on-struct-definition-without-fixing-one-of-the-fn-par), which has the same problem with a struct definition. It doesn't work because argument types are parameters of `Fn`, not associated types -- there can be more than one `Fn` trait impl for some type. – trent Sep 20 '18 at 15:44
  • I suspect the problem is related to variance. In Rust, variance is only about lifetimes, and it can be harder to think about than variance in inheritance-based subtyping. – Peter Hall Sep 20 '18 at 15:44
  • 1
    In the second case `State` is in argument position, which means that any `F` that meets the requirements for this impl, could actually require a more general type (super-type) than specified. In the case of reference types, that means a shorter lifetime. I think the problem is that the borrow checker won't be able to figure out the lower bound on that lifetime. I'm not 100% certain of this though. – Peter Hall Sep 20 '18 at 15:49

0 Answers0