No, you don’t, know the concrete type indeed, so you should probably avoid building an API that’s so generic that those ambiguities appear in the first place.
In case you’re wondering, is it possible to use at all? Yeah, use a generic wrapper that can name the generic type, like
fn new_some_task_pinned<'a, F: Future>(f: Pin<&'a mut F>) -> SomeTask<'a, F> {
SomeTask::new(f) // not ambigous because of return type above
}
async fn test_some_task() {
let f1 = async { 123 };
let t1 = SomeTask::new(f1);
let f2 = async { 456 };
let p2 = pin!(f2);
let t2 = new_some_task_pinned(p2);
}
does this kill all intended ergonomics of your API completely? I’d guess: also, yes – you now could’ve written let t2 = SomeTask::Pinned(p2); to begin with.
The first case, you have a type – let’s call it – MyAsyncBlock and f1: MyAsyncBlock and impls
impl<'a, G> From<G> for SomeTask<'a, G> impl<'a, H> From<Pin<&'a mut H>> for SomeTask<'a, H>
Then MyAsyncBlock fits the input type of only one of these impls:
the first one, with G = MyAsyncBlock so that G fits the type of f1.
under this substitution, the impl looks like impl<'a> From<MyAsyncBlock> for SomeTask<'a, MyAsyncBlock>
The second case, you have a type MyAsyncBlock2 but construct p2: Pin<&'l mut MyAsyncBlock2>[1] from it. Now this type of p2 fits the input types of both of the impls, respectively:
the first one, with G = Pin<&'l mut MyAsyncBlock2>, so that G fits the type of p2, and
under this substitution, the impl looks like impl From<Pin<&'l mut MyAsyncBlock2>> for SomeTask<'a, Pin<&'l mut MyAsyncBlock2>>
the second one, with H = MyAsyncBlock2,[2] so that Pin<&'l mut H> fits the type of p2.
under this substitution, the impl looks like impl From<Pin<&'l mut MyAsyncBlock2>> for SomeTask<'l, MyAsyncBlock2>