Associated type issue in blanket impl for extension trait

I came across a strange trait bounds not satisified error while playing with an extension trait today, I reduced it to this minimum snippet (also playground):

/// basically `FnOnce(From)->To`
trait Mapper<From> {
	type To;
	fn map(self, from: From) -> Self::To;
}
trait OptionExt {
	type T;
	fn map2<F1, F2>(self, f1: F1, f2: F2) -> Option<<F2 as Mapper<F1::To>>::To>
	where
		F1: Mapper<Self::T>,
		F2: Mapper<F1::To>;
}
impl<T> OptionExt for Option<T> {
	type T = T;
	fn map2<F1, F2>(self, f1: F1, f2: F2) -> Option<<F2 as Mapper<F1::To>>::To>
	where
		F1: Mapper<Self::T>,
		F2: Mapper<F1::To>,
	{
		todo!()
	}
}

error message:

error[E0277]: the trait bound `F1: Mapper<T>` is not satisfied
  --> src/lib.rs:14:2
   |
14 | /     fn map2<F1, F2>(self, f1: F1, f2: F2) -> Option<<F2 as Mapper<F1::To>>::To>
15 | |     where
16 | |         F1: Mapper<Self::T>,
17 | |         F2: Mapper<F1::To>,
   | |___________________________^ the trait `Mapper<T>` is not implemented for `F1`

I use Option in this example, my original code is for a third party library type. for now, I can make it to compile by adding an equality constraint like this:

trait OptionExt {
	type T;
	fn map2_<T2, F1, F2>(self, f1: F1, f2: F2) -> Option<<F2 as Mapper<T2>>::To>
	where
		F1: Mapper<Self::T, To = T2>,
		F2: Mapper<T2>;
}

anyone knows is this the best way (or only way) to work around this issue? what caused the error in the first place?


additional context: I encountered this problem when I update my code from using FnOnce to a custom trait (the Mapper in the above example) for more features. since the sugar syntax of FnOnce force me to name the intermediate type, this error didn't come up in the old code, which is also how I find the workaround.

I believe it's this or one of the similar issues (search for "E0277 stricter"). Note that "fixed by next solver" label is no longer accurate for the OP of that issue (nor yours).

thanks for the pointer, I read several reports using your keyword, so the common pattern I found seems to be:

  1. Self::AssociatedType is mentioned in a bound for a generic type, such as F1: Mapper<Self::T>, and...
  2. this bound also has an associated type like <F1 as Mapper<Self::T>>::To, and...
  3. this projection is mentioned in another bound.

if a trait method meet all 3 condition, then it is unimplementable (except for a default implementation inside the trait definition itself, if possible, example below).

the report #41118 has the bare minimum to trigger this diagnostic, I copied it here to break it down:

// this is the `Mapper` trait in my example
trait Foo<T> {
   type Quux; //<-- condition 2: trait `Foo` has an associated type
}
// the trait itself is parameterized by `B`, while in my example
// the generic type `F1` is on the method.
trait Bar<B> {
    type Baz;
    fn arthur()
    where
        B: Foo<Self::Baz>, //<-- condition 1: `Self::Baz` is mentioned in bound `Foo<...>`
        B::Quux: Send; //<- condition 3: projection `B::Quux` is mentioned in another bound `Send`
}

it so happens that I find another workaround for my use case: the trait method is unimplementable in an impl block, no matter for a concrete type or a blanket implementation, but if the method can have a default implementation, i.e. if it is some form of "template" that can be decomposed into multiple simpler steps on Self, then you can implement it inline at the definition site:

trait OptionExt {
    type T;
    fn map1<F: Mapper<Self::T>>(self, f: F) -> Option<<F as Mapper<Self::T>>::To>;
    fn map2<F1, F2>(self, f1: F1, f2: F2) -> Option<<F2 as Mapper<F1::To>>::To>
    where
        Self: Sized,
        F1: Mapper<Self::T>,
        F2: Mapper<F1::To>,
    {
        self.map1(f1).map1(f2)
    }
}
impl<T> OptionExt for Option<T> {
    type T = T;
    fn map1<F: Mapper<T>>(self, f: F) -> Option<F::To> {
        todo!()
    }
}