-
Notifications
You must be signed in to change notification settings - Fork 12.7k
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
Shouldn't lifetime elision apply to closure return type annotation? #56537
Comments
Potentially related bugs (at least in terms of comparatively evaluating our specified guarantees and/or our currently implemented behavior):
|
Ugh. Here is some code, adapted from #23340, that my "fix" to this is going to "break": fn print_first(list: Vec<String>) {
let x: &str = list
.first()
.map(|s| -> &str { &s[..] })
.unwrap_or("");
println!("First element is {}", x);
} That is, without the change described on this issue, the compiler accepts the above code. But if I add the change so that lifetime elision is applied to the
|
the reason I wrote "at least in a naive fashion" is that the correct generalization of the lifetime elision rules to cover closure expressions is not trivially obvious. Why? Because those rules rely on definitions of lifetime positions in input/output, but the definitions given do not account for occurrences of the wildcard type And I might argue that it is the implicit occurrence of the wildcard type |
But one way to generalize the rules in a manner that maintains backward compatibility might be to say: If there are any wildcard types This may yield surprising differences in behavior for end-users depending on which types annotations are supplied and which are not. But I think that is inevitable when you get into the weeds like this. |
I left some thoughts on Zulip: The TL;DR is that "annotations on closures are tricky". It feels like lifetime elision should apply (and I argued this before) -- but it's clear that can break code in light of how we handle the expected/provided split. Right now, if there is an expected signature I could imagine trying to "defer" the elision rules until we know if there is an expected type...but ugh. |
yet another option would be to sidestep trying to resolve this via elision, and instead focus attention on adding support for an expression form |
By the way... I wrote:
and @nikomatsakis subsequently referenced some comments on Zulip, which included this:
To make this quite concrete, I made the following example (play) fn willy<'w>(p: &'w str) -> &'w str {
let free_dumb = |x: &str| -> &str { p };
let hello = format!("Hello");
free_dumb(&hello)
}
fn main() {
let world = format!("World");
willy(&world);
} In the above, the closure assigned to And indeed, the compiler today accepts that code as written. While my naive change to the compiler rejects it, with this error message from AST-borrowck:
and this error message from NLL borrowck:
|
if nothing else, I would like to encode the examples from the comments on this issue into tests that we put into our test suite. Because right now, if I add the naive change to apply lifetime elision rules to |
Okay, I have posted PR #56746 which encodes the examples from my comments here in a formal compile-pass test. With that, I think I am convinced that we cannot immediately change our lifetime elision rules to apply to closure expressions. @nikomatsakis has suggested that we might in the future add some other way to encode the signature of lambda expressons such that it would allow the user to indicate that lifetime elision should be applied. But such an addition is not going to happen via the current Given that constraint (that we cannot just add this via the current syntax), I am going to close this issue. |
…osure-using-region-from-containing-fn, r=nikomatsakis Add test of current behavior (infer free region within closure body) This behavior was previously not encoded in our test suite. it is pretty important that we test this behavior. In particular, in rust-lang#56537 I had proposed expanding the lifetime elision rules so that they would apply to some of the cases encoded in this test, which would cause them to start failing to compile successfully (because the lifetime attached to the return type would start being treated as connected to the lifetime on the input parameter to the lambda expression, which is explicitly *not* what the code wants in this particular case). In other words, I am trying to ensure that anyone who tries such experiments with lifetime elision in the future quickly finds out why we don't support lifetime elision on lambda expressions (at least not in the naive manner described on rust-lang#56537).
…osure-using-region-from-containing-fn, r=nikomatsakis Add test of current behavior (infer free region within closure body) This behavior was previously not encoded in our test suite. it is pretty important that we test this behavior. In particular, in rust-lang#56537 I had proposed expanding the lifetime elision rules so that they would apply to some of the cases encoded in this test, which would cause them to start failing to compile successfully (because the lifetime attached to the return type would start being treated as connected to the lifetime on the input parameter to the lambda expression, which is explicitly *not* what the code wants in this particular case). In other words, I am trying to ensure that anyone who tries such experiments with lifetime elision in the future quickly finds out why we don't support lifetime elision on lambda expressions (at least not in the naive manner described on rust-lang#56537).
…osure-using-region-from-containing-fn, r=nikomatsakis Add test of current behavior (infer free region within closure body) This behavior was previously not encoded in our test suite. it is pretty important that we test this behavior. In particular, in rust-lang#56537 I had proposed expanding the lifetime elision rules so that they would apply to some of the cases encoded in this test, which would cause them to start failing to compile successfully (because the lifetime attached to the return type would start being treated as connected to the lifetime on the input parameter to the lambda expression, which is explicitly *not* what the code wants in this particular case). In other words, I am trying to ensure that anyone who tries such experiments with lifetime elision in the future quickly finds out why we don't support lifetime elision on lambda expressions (at least not in the naive manner described on rust-lang#56537).
…osure-using-region-from-containing-fn, r=nikomatsakis Add test of current behavior (infer free region within closure body) This behavior was previously not encoded in our test suite. it is pretty important that we test this behavior. In particular, in rust-lang#56537 I had proposed expanding the lifetime elision rules so that they would apply to some of the cases encoded in this test, which would cause them to start failing to compile successfully (because the lifetime attached to the return type would start being treated as connected to the lifetime on the input parameter to the lambda expression, which is explicitly *not* what the code wants in this particular case). In other words, I am trying to ensure that anyone who tries such experiments with lifetime elision in the future quickly finds out why we don't support lifetime elision on lambda expressions (at least not in the naive manner described on rust-lang#56537).
…osure-using-region-from-containing-fn, r=nikomatsakis Add test of current behavior (infer free region within closure body) This behavior was previously not encoded in our test suite. it is pretty important that we test this behavior. In particular, in rust-lang#56537 I had proposed expanding the lifetime elision rules so that they would apply to some of the cases encoded in this test, which would cause them to start failing to compile successfully (because the lifetime attached to the return type would start being treated as connected to the lifetime on the input parameter to the lambda expression, which is explicitly *not* what the code wants in this particular case). In other words, I am trying to ensure that anyone who tries such experiments with lifetime elision in the future quickly finds out why we don't support lifetime elision on lambda expressions (at least not in the naive manner described on rust-lang#56537).
…osure-using-region-from-containing-fn, r=nikomatsakis Add test of current behavior (infer free region within closure body) This behavior was previously not encoded in our test suite. it is pretty important that we test this behavior. In particular, in rust-lang#56537 I had proposed expanding the lifetime elision rules so that they would apply to some of the cases encoded in this test, which would cause them to start failing to compile successfully (because the lifetime attached to the return type would start being treated as connected to the lifetime on the input parameter to the lambda expression, which is explicitly *not* what the code wants in this particular case). In other words, I am trying to ensure that anyone who tries such experiments with lifetime elision in the future quickly finds out why we don't support lifetime elision on lambda expressions (at least not in the naive manner described on rust-lang#56537).
…osure-using-region-from-containing-fn, r=nikomatsakis Add test of current behavior (infer free region within closure body) This behavior was previously not encoded in our test suite. it is pretty important that we test this behavior. In particular, in rust-lang#56537 I had proposed expanding the lifetime elision rules so that they would apply to some of the cases encoded in this test, which would cause them to start failing to compile successfully (because the lifetime attached to the return type would start being treated as connected to the lifetime on the input parameter to the lambda expression, which is explicitly *not* what the code wants in this particular case). In other words, I am trying to ensure that anyone who tries such experiments with lifetime elision in the future quickly finds out why we don't support lifetime elision on lambda expressions (at least not in the naive manner described on rust-lang#56537).
I've filed #86921 to discuss the prospect of using a future edition to make this incompatible fix to the closure elision rules. |
(Spawned off of #55526.)
Consider the following code (play):
The above does not compile today.
Error diagnostic:
However, discussions with @nikomatsakis lead us to wonder why lifetime elision is not being applied to the type annotation attached to the closure. Since there is a single input argument type and a single return type, it seems reasonable to apply lifetime elision, yielding the type
for <'r> (&'r str) -> &'r str
.I believe fixing this is actually pretty trivial, based on some initial experimentation with the code here:
rust/src/librustc/middle/resolve_lifetime.rs
Lines 2112 to 2117 in 21cb46a
(The question will only be whether there are unintended consequences of the fix; I'm still looking into that.)
(This might require an RFC, not sure yet.)
The text was updated successfully, but these errors were encountered: