-
Notifications
You must be signed in to change notification settings - Fork 12.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Annotating higher-ranked lifetimes on closures is arduous #58052
Comments
I think that: fn main() {
let f = |x: &i32| x;
let f = |x: &i32| -> &i32 { x };
let f: impl for<'a> Fn(&'a i32) -> &'a i32 = |x| x;
let f = for<'a> |x: &'a i32| -> &'a i32 { x };
let i = &3;
let j = f(i);
} should all be equivalent (well ~ the opaque nature of ISTM that As for introducing |
👍 It will be very nice to have higher-ranked lifetimes for closure. (Very nice to write easily schedulable tasks) |
Is there any plans for smth. like |
We don't presently have clear plans to support this syntax, although we are actively at work on extending rustc internally to support that sort of thing. |
#98705 implemented the closure lifetime binder syntax. However, the syntax requires the user to fully specify the type signature, so I'd argue that using higher-ranked lifetimes on closures are still tedious. |
Another use case where one runs into this issue: fn print_ref(value: &i32) {
println!("{value}");
}
fn main() {
let f = |x| print_ref(x);
{
let y = 1;
f(&y);
}
{
let z = 2;
f(&z);
}
} Compile error
However, for some reason, annotating the type with The workaround that compilesfn print_ref(value: &i32) {
println!("{value}");
}
fn main() {
let f = |x: &i32| print_ref(x);
{
let y = 1;
f(&y);
}
{
let z = 2;
f(&z);
}
} |
Executive summary: If you want to make a closure returning a higher-ranked lifetime, you need to use a helper like
fn annotate<T,F>(f: F) -> F where F: Fn(&T) -> &T { f }
. We could probably do better.Spawned off of #22557 (comment) (and possibly a duplicate of #22340 )
Consider this code (play):
It doesn't compile. And its diagnostic is pretty hard to understand.
You can see an explanation from @nikomatsakis about why it doesn't compile here: #22557 (comment)
With
#![feature(nll)]
, it still doesn't compile; the diagnostic is slightly better:The aforementioned explanation claims that adding a return type will get it to compile. But when I tried that, it did not work, both with and without
#![feature(nll)]
(play, which includes NLL since I like its diagnostic here better):yields (and we'll leave #58053 in its own bug):
So what gives? Well, I think when @nikomatsakis claimed that an explicit return type would work, they were assuming that an explicit return type would cause lifetime elision rules to apply such that the same lifetime would be provided for the input and output reference-types on
f
. But as we saw in #56537, lifetime elision rules do not apply to closure return type annotations.So what we want is to say that we have a lifetime parametric closure, with a type something like
for<'a> Fn(&'a i32) -> &'a i32
. But no, that's not a type, its a trait bound, so this does not work either (play):yields:
An approach that does work is to feed in the trait bound explicitly via a helper function, like this (play):
But that seems like a pretty arduous way to encode a relatively simple pattern. If you look at #22340 (comment), you can see others have suggested syntaxes like
for <'a> |x: &'a i32| -> &'a i32 { x }
, (where thefor <'a> CLOSURE_EXPR
is the new interesting expression form), which would be more convenient for addressing cases like ths.The text was updated successfully, but these errors were encountered: