async fn lifetime eliding satisfies HRTB where explicit does not #114947
Open
Description
opened on Aug 17, 2023
In this code, two generators are defined. Their definitions should be equivalent. The async-fn variant where lifetimes are elided works fine, but the variant where the lifetimes are defined explicitly with an impl-trait return fails to compile against the HRTB.
Playground with more definitions to actually build.
trait AsyncFnOnce<Arg> {}
impl<F, A, B, R: Future> AsyncFnOnce<(A, B, )> for F
where
F: FnOnce(A, B) -> R,
{}
pub unsafe trait RemitWithLifetime<T, X> {}
unsafe impl<T, X, F> RemitWithLifetime<T, (X, )> for F
where
F: for<'a> AsyncFnOnce<(X, Remit<'a, T>, )>,
{}
impl<T, P> Generator<T, P> {
pub fn parameterized<'s, G, X>(
self: Pin<&'s mut Self>,
_gen: G,
_parameter: X,
) -> GeneratorIterator<'s, T, P>
where
// insures fn is not implemented only for 'static
G: RemitWithLifetime<T, (X,)>,
// insures P is properly defined, even if it actually has a lifetime
G: FnOnce(X, Remit<'static, T>) -> P,
{
unimplemented!();
}
}
use std::pin::pin;
fn only_build() {
let data = String::from("hi");
/// Implicit elision works
async fn gen_implicit(data: &str, remit: Remit<'_, usize>) {
remit.value(data.len()).await;
remit.value(data.len()).await;
}
for item in pin!(Generator::new()).parameterized(gen_implicit, &data) {
dbg!(item);
}
/// Does not work, as explicit lifetime definitions fail HRTB.
fn gen_explicit<'a: 'c, 'b: 'c, 'c>(data: &'a str, remit: Remit<'b, usize>) -> impl std::future::Future<Output=()> + 'c {
async move {
remit.value(data.len()).await;
remit.value(data.len()).await;
}
}
for item in pin!(Generator::new()).parameterized(gen_explicit, &data) {
dbg!(item);
}
}
Both gen_implicit
and gen_explicit
should have been able to be used successfully.
Instead, there is a compiler error:
error: implementation of `FnOnce` is not general enough
--> src/lib.rs:49:17
|
49 | for item in pin!(Generator::new()).parameterized(gen_explicit, &data) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough
|
= note: `fn(&str, Remit<'2, usize>) -> impl Future<Output = ()> + '_ {gen_explicit::<'_, '2, '_>}` must implement `FnOnce<(&str, Remit<'1, usize>)>`, for any lifetime `'1`...
= note: ...but it actually implements `FnOnce<(&str, Remit<'2, usize>)>`, for some specific lifetime `'2`
Activity