Code change:
This fails with: I believe this should compile because: non-async function with the same signature does compile; insignificant tweaks, like adding a wrapper struct, make it compile. Mentioning who fixed a similar error message in . Mentioning who touched this error message in and . rustc 1.38.0-nightly ( 2019-07-26) <!-- TRIAGEBOTSTART --<!-- TRIAGEBOTASSIGNSTART --<!-- TRIAGEBOTASSIGNDATASTARTTRIAGEBOTASSIGNDATAEND --<!-- TRIAGEBOTASSIGNEND --<!-- TRIAGEBOTEND --
Check-in from WG-async-await meeting: I agree this is a bug -- it doesn't necessarily block stabilization (no fwd compat risk) but would definitely be a good one to fix. Marking as blocking + unclear for the moment and will return to it.
I did a bit of investigation. The error results because we have an inference variable () that seems to have no constraints, but must be equal to one of the captured regions (e.g., ). I haven't 100% figured out where this variable comes from, I imagine it's related to though? In any case, the resolver basically winds up in a situation where it could pick any of the lifetimes but has no reason to prefer one over the other, and hence it reports an error.
OK, I think I see what's going on. It doesn't actually have much to do with the unused lifetime per se. What happens is this: The type of the generated result is something like -- i.e., it includes the types of the parameters, though with region variables in place of the known regions. Those types must be supertypes of the actual parameter types. We capture the argument of type -- per the subtyping rules, we create the type for the type of this captured value, with the requirement that -- as it happens, this is always true, so there is effectively no constraint on . We do impose the constraint that and must both be equal to one of the free regions -- in this case, or the anonymous lifetime of (which I'll call ). So thus the region solvers get in a state where must be equal to , , or -- but any one of them would be a reasonable choice. Removing the unused lifetime () makes this an easy choice -- it can pick , which is smaller than . (Per the "least choice" rules we usually use.) Removing the type and putting it into a struct () removes the lifetime variable altogether, as then we have , and there are no lifetime parameters involved. I'm not sure what I think is the best fix. It would be nice for the solver to pick , though it complicates the algorithm. You could certainly imagine a tweak that picks if the value is legal and otherwise looks for a least choice. That might indeed be superior to the current approach, though I'd want to think about the full implications -- in particular, when is used in places outside of the fn return type (e.g., in a statement), picking might lead to unnecessary borrow check errors. (The alternative would be to only look for when there is no least choice; that seems safer.) (Interestingly, at some point at least I thought that a polonius-like approach sidesteps a lot of the challenges here-- if we had confidence that we would transition that might ease some of my concerns.)
Now that I understand better what is happening, I don't feel this has to block stabilization -- in particular, I don't think that there is a forward compatibility risk from us improving the region inference algorithm. I don't think we would choose an algorithm that makes existing code illegal. =) As such, I'm going to mark this as Deferred.
Here's another example of the same problem that doesn't have an unused lifetime (AFAIKT): Removing any of the three arguments to the function allows it to compile.
I am also affected by this bug, where having both a reference and a box as parameters to a function causes the cryptic "hidden type for captures lifetime that does not appear in bounds" : () Is there a resolution in sight ?
is unused in that function - if you remove the it compiles.
Ok, let me change it to something closer to what I had in my original code: ()
I think I found another example of this: () This example more or less represents what I met in my code (when I tried to pass a structure with a type parameter to an async function with the default value for this type parameter being a trait object), but it can be simplified by removing the default parameter assignment and passing the argument to explicitly: () Curiously, if you replace with , the error goes away: () It is important to note that the function has two additional arguments. If you remove one or both of them, it starts compiling again: ()
's comment is a minimal reproduction of what I ran into. I had a function . If I remove two of the cache arguments it compiles. As soon as I add one back in it gives me the title error.
Hmm, it is probably worth revisiting this problem now that stabilization has happened.
Any word on this? I'm getting this error as well. Changing to fixes the issue. Only problem is that I actually want to move that vector in because I want to each call to .
No progress or major updates. The is still accurate as to the root cause, but I think we don't yet know the best fix.
this is going to become a problem now that is merged. It makes it impossible to implement for types with lifetimes, when the future that's returned is unnameable. consider
Here's another very simple example: Removing any of the 3 parameters makes the code compile. Alternatively, removing the static lifetime of (→ ) makes it compile as well.
Hi. I get the following error that matches what everyone is also getting:
I have the following case triggering this error message:
Got the error in the following: Removing helps. rustc 1.50.0-nightly ( 2020-12-09)
I know I am late to the game, but this issue still hunted us down with a Rust 1.48 compiler. The workaround is simple, but it pollutes all the callers with extra lifetime parameters. Basically, wherever saved us from introducing a lifetime parameter, we have to introduce it in s, s, everywhere. () If someone wants to workaround this issue without polluting the callers, they could use an async block inside a regular function: () Maybe if you treat special in the async function implementation, like in other parts of the language, that would cover most of the cases where this issue is hit.
Another short example based on reducing code from a discord question:
And 2 workarounds for the issue:
Found this after pulling my hair out for a few hours. At least I've got a workaround now, but I'd have to say this behavior is really unexpected. Of those two workarounds mentions only the first seems to be actually usable if you actually want to do something with those parameters: // Compiles async fn foo<'a(x: &'static str, y: &'a str, z: &'a str) { println!("{} {}", y, z); } // Doesn't compile: // error[E0759]: has an anonymous lifetime but it needs to satisfy a lifetime requirement fn bar(x: &'static str, y: &str, z: &str) -impl Future<Output=(){ async { println!("{} {}", y, z); } }
I'm going to remove the "async await triaged" label so that we take a second look at this. Maybe worth reprioritizing. I'll see if I can post some mentoring instructions.
Just hit this as well.
claim I'm claiming this to write some mentoring instructions
FWIW, in the meantime, and also to help solve the issue with manual capturing a potentially bigger invariant lifetime, I have published a helper proc-macro-attribute crate that automatically appends the necessary hacks:
Coming to this too, a little google drives me here. This thread is almost 2 years old, is there any solution or plan to fix this?
explicit lifetimes on references should fix the issue for you until compiler is smart enough to figure that out on its own
release-assignment I haven't had time to write up any mentoring instructions here, I'm afraid. :(
Big sad. I wish I was more into the compiler details. I love what async-await did to learnability for newbies and this issue hunts down them after getting their first successes.
I want to ensure that, is it because the readloop function might execute after the scope of self reference? If so, how can I explicitly set the lifetime of readloop is greater than the reference?
note that your code can be automatically fixed by the tool I mentioned in : ! A reliable fix that doesn't change the semantics of the function You can even query that attribute with the extra parameter fed to the attribute, and the eponymous Cargo feature, to get: <img width="681" alt="Screen Shot 2021-07-05 at 14 36 38" src="https://user-"For the specific case of usage, you can try changing the signature of the function to one using , that is, with the bound on the trait object explicitly spelled out: This leads to a simpler signature regarding / / TAIT unsugaring, and circumvents the issue of this thread. _ I'd like to experiment with unsugared's TAIT being automatically generic over all the lifetime parameters in scope of the return type. This is what achieves by using the hack for each and every such lifetime, and I've never encountered cases where this would be an error. If that approach seems fine to you, I can try to implement this new unsugaring on the compiler myself, although I may not be very fast since I'll need to experiment a bit to get acquainted with the related compiler internals
thanks, I will try that.
I just got bitten by this and I'm a beginner. 's workarround worked but a proper fix would be desirable.
are you saying you want to change how works for all cases? That would certainly be a breaking change, so I don't think we can do that. However, it would be good to talk about a better fix than the hack. One thing we talked about in the very beginning was having a syntax like to be very explicit about what things are captured and what are not. But that would, I think, require a lang-team proposal.
claim
When I thought about this I struggled to come up with an instance of this, so I'm genuinely curious about it. In other words: could annotating a function with / add lead to breakage somewhere along the way? If so, such an example could help us evaluate the different options at our disposal
OK, I've read up on this issue again. The basic diagnosis I had is correct. The fact that some of the lifetimes are unused isn't really especially relevant. For example, in the , the problem is simply that there are two lifetimes (from and ) along with the . This still gets us into the situation where the inferencer must pick between , , or . Let me run this through the compiler and take a look at the constraints. I also want to think about how polonius affects this.
I just read over . The reason that this "desugared" version works is because it introduces a new lifetime, , and this lifetime has a relationship to every other lifetime. So when we have to pick between , , and , we now also haev the choice of , which is strictly less than all of those choices. So we pick that. Now I realize that the reason that this whole situation is a non-issue in polonius is basically that the polonius representation permits lifetimes that are the intersection of a set of lifetimes. So you could infer the result to be , which is effectively the same as . But in the current implementation, we don't have anything like that.
I'm not sure if I understand exactly what you were proposing here, I thought at first that it meant: would be able to capture (because it would capture that lifetime). That would be a breaking change, but is that what you meant?
Fix in
, effectively defining it, for all intents and purposes, as the intersection lifetime! (which indeed meets the bounds) Ah yes, I see now how there are definitely two distinct issues at hand, here, and that my fix for one just happened to fix the other as a byproduct _ So, while your PR will fix the one, I'm personally still concerned about the case of the existential capturing both , and it's to tackle that one that I was suggesting that existentials were, internally, allowed to be generic over all the lifetime parameters in scope. That is, what I meant, or what I'm basically qualifying as a bug, is the fact that and don't have the same semantics, lifetime-wise (so, to clarify, I am definitely not talking about changing the rules for lifetime elision in function signatures): In the same fashion that stands for , and can thus not capture since it is -bound,then stands for and can thus not capture yadda yadda. And in the case, the is not needed, so it can be skipped if the implementation doesn't need it. But the key change I am suggesting is the definition of that existential. Currently, it would be defined as something like , and then unified. What I'm suggesting is that it be defined as , much like what adding a would achieve. This would be meaningful, for instance, in all the cases with a captured invariant lifetime which is not equal to the lower bound. Take , and . In that case, if instead of we had , the signature would work. But in the case, we have a , and such a type definition cannot be unified with the actual type of the implementor, , since there is no in scope of that definition. My observation is that I believe it cannot hurt to have too many lifetime parameters "in scope" / reachable for an existential's type definition. In that regard, the hack, be it manual or automated through the attribute, is just a way to test my theory: the attribute currently adds bounds for every lifetime it meets, without asking further questions, and that suffices to make the existential type definition be generic over all of them, and this whole thing has, empirically, solved the compilation problems without any problems on its own, thus allowing for and to remain quite interchangeable (lifetime-wise, barring obvious safety caveats).<sub<sup(Slight aside: I've noticed that this , so it's really just the lifetime parameters that it's being (overly) picky around, it seems)</sup</sub
Just hit this as well.
Encountered this today. Though the original error was this, which I couldn't reproduce it in playground. Removing any of the parameters the error is gone rustc 1.66.0-nightly ( 2022-10-01)

Related discussion:
text: } debug!(?choice_regions, "after ub"); <del> // If we ruled everything out, we're done. if choice_regions.is_empty() { return false; } // Otherwise, we need to find the minimum remaining choice, if // any, and take that. debug!("choice_regions remaining are {:#?}", choice_regions); let Some(&min_choice) = choice_regions.iter().find(|&r1| { </del> <ins> // At this point we can pick any member of `choice_regions`, but to avoid potential // non-determinism we will pick the *unique minimum* choice. // // Because universal regions are only partially ordered (i.e, not every two regions are // comparable), we will ignore any region that doesn't compare to all others when picking // the minimum choice. // For example, consider `choice_regions = ['static, 'a, 'b, 'c, 'd, 'e]`, where // `'static: 'a, 'static: 'b, 'a: 'c, 'b: 'c, 'c: 'd, 'c: 'e`. // `['d, 'e]` are ignored because they do not compare - the same goes for `['a, 'b]`. let totally_ordered_subset = choice_regions.iter().copied().filter(|&r1| { </ins> choice_regions.iter().all(|&r2| { <del> self.universal_region_relations.outlives(r2, *r1) </del> <ins> self.universal_region_relations.outlives(r1, r2) || self.universal_region_relations.outlives(r2, r1) </ins> }) <ins> }); // Now we're left with `['static, 'c]`. Pick `'c` as the minimum! let Some(min_choice) = totally_ordered_subset.reduce(|r1, r2| { let r1_outlives_r2 = self.universal_region_relations.outlives(r1, r2); let r2_outlives_r1 = self.universal_region_relations.outlives(r2, r1); match (r1_outlives_r2, r2_outlives_r1) { (true, true) => r1.min(r2), (true, false) => r2, (false, true) => r1, (false, false) => bug!("incomparable regions in total order"), } </ins> }) else { <del> debug!("no choice region outlived by all others"); </del> <ins> debug!("no unique minimum choice"); </ins> return false; };