How to specify a generic argument for a type that implements trait Fn when the args are not constrained?

Viewed 59

I am trying to implement a trait using a "map" concept. I've came up with the following minimal example:

trait Value<T> {
    fn get(&self) -> T;
}
struct ValueMap<S, F> {
    s: S,
    f: F,
}
impl<T, U, S: Value<T>, F: Fn(T) -> U> Value<U> for ValueMap<S, F> {
    fn get(&self) -> U {
        (self.f)(self.s.get())
    }
}

I get the error the type parameterTis not constrained by the impl trait, self type, or predicates.

How may I implement the Value trait for my ValueMap struct when F is a function that maps the value S to something else?

Remarks: I don't have this issue when I use associated types on Value. But the concepts are still a bit blurry for me.

1 Answers

The details for error message #0E207 say that:

Any type parameter or lifetime parameter of an impl must meet at least one of the following criteria:

  • it appears in the implementing type of the impl, e.g. impl<T> Foo<T>
  • for a trait impl, it appears in the implemented trait, e.g. impl<T> SomeTrait<T> for Foo
  • it is bound as an associated type, e.g. impl<T, U> SomeTrait for T where T: AnotherTrait<AssocType=U>

None of these hold for your T. So what you're doing is currently not supported.

It feels hacky, but I was able to get this to work by adding additional type parameters to ValueMap, backed by phantom data members. This way each of the types does occur in the imlementing type, and the requirements are satisfied.

trait Value<T> {
    fn get(&self) -> T;
}

struct ValueMap<T, U, S, F>
where
    F: Fn(T) -> U,
{
    s: S,
    f: F,
    _t: std::marker::PhantomData<T>,
    _u: std::marker::PhantomData<U>,
}

impl<T, U, S, F> Value<U> for ValueMap<T, U, S, F>
where
    S: Value<T>,
    F: Fn(T) -> U,
{
    fn get(&self) -> U {
        (self.f)(self.s.get())
    }
}

Playground link

Related