In-band lifetimes: Lint against single-use lifetime names

6b80e55
Opened by Niko Matsakis at 2025-03-23 08:52:47

Once support for '_ lands in https://github.com/rust-lang/rust/pull/44691, the next step for https://github.com/rust-lang/rust/issues/44524 is to implement a lint that warns against "single use" lifetime names.

Current status

The lint is partially implemented but needs to be completed. Here is a checklist:

  • [x] Issue warnings at the right times; this gist provides a comprehensive test case.
    • "Warning: never used at all" falls under unused_lifetimes
  • [x] For each warning, issue a suggested fix:
    • For a single-use lifetime 'a appearing in &'a T, suggest &T
    • For a single-use lifetime 'a appearing in any other place, suggest '_
    • One challenge: the binder must be removed too
      • cc @estebank -- is it possible to give suggested fixes that make changes to multiple spots at once? I guess that might just be multiple suggestions?
  • [] We'll know this is really done when we can enable by default in rustc crates and apply rustfix with suggestions

Older Background

The idea is that an explicit name like 'a should only be used (at least in a function or impl) to link together two things. Otherwise, you should just use '_ to indicate that the lifetime is not linked to anything.

Until https://github.com/rust-lang/rust/issues/15872 is closed, we should only lint for single-use lifetime names that are bound in functions. Once #15872 is closed, we can also lint against those found in impl headers.

We can detect cases where a lint is valid by modifying the resolve_lifetimes code:

  • This code basically walks over the HIR and resolves all lifetime names.
  • It maintains a stack of scopes indicating what names are valid.
  • The function with() is used to push new scopes on the stack. It is also given a closure which will execute with the new name bindings in scope.
    • with() gets called for impls and other kinds of items from here; for methods and functions in particular it is called from visit_early_late).
  • Once a name is in scope, resolve_lifetime_ref() is called to resolve an actual reference to a named lifetime.
    • This could be used, for example, to update some information in the Scope, e.g. counting how many times a particular lifetime was referenced.
  • Then, before with() returns, we could scan the lifetimes and check for those that were only referenced 1 time (or 0 times...) and issue a lint warning.

(There are some directions for how to add a lint under the header "Issuing future compatibility warnings" in the rustc-bug-fix-procedure page on forge -- we can skip the "future compatibility" parts here.)

  1. we should only lint for single-use lifetime names that are bound in functions

    Also, I believe, only for lifetime names that appear in argument position. Currently we require explicitly binding output-only lifetimes with a name:

    fn bar<'a>() -> &'a u8 { &5 } // OK
    fn bar() -> &'_ u8 { &5 } // ERROR: missing lifetime specifier
    

    Taylor Cramer at 2017-09-22 00:28:00

  2. @cramertj Hmm. I would consider the proper way to declare bar() to probably be fn bar() -> &'static u8, really, though there are subtleties involved in rare cases (e.g., around invariance).

    Niko Matsakis at 2017-09-22 15:01:28

  3. @nikomatsakis Yes, 'static is the right thing to use. I wanted to point out that we shouldn't recommend '_ here, as it won't work.

    Taylor Cramer at 2017-09-22 16:09:29

  4. Is this up for grabs?

    Gauri Kholkar at 2017-09-30 18:41:48

  5. @gaurikholkar Go for it!

    Taylor Cramer at 2017-10-03 01:01:12

  6. Will start working on it

    Gauri Kholkar at 2017-10-03 13:03:19

  7. @gaurikholkar hey, just checking in! How's it going? Any blockers?

    Niko Matsakis at 2017-10-16 13:49:46

  8. Some examples:

    fn foo<'x>(_: &'x u32) { }
    

    This should lint against 'x and suggest &u32 instead.

    struct Foo<'a> { x: &'a u32 }
    fn foo<'x>(_: Foo<'x>) { }
    

    This should lint against 'x and suggest Foo<'_> instead.

    struct Foo<'a, 'b> { f: &'a &'b u32 }
    fn foo<'x, 'y>(foo: Foo<'x, 'y>) -> &'x u32 { foo.f }
    

    This should lint against 'y and suggest Foo<'x, '_> instead.

    struct Foo<'a, 'b> { f: &'a &'b u32 }
    fn foo<'x, 'y>(foo: Foo<'x, 'y>) -> &'y u32 { foo.f }
    

    This should lint against 'x and suggest Foo<'_, 'y> instead.

    fn foo<'x>() -> &'x u32 { &22 }
    

    This should not lint, because 'x appears only in the return type.

    trait Trait<'a> { }
    impl<'a, T> Trait<'a> for T { }
    
    fn foo<'x, T>(t: T)
    where T: Trait<'x>
    { 
    }
    
    fn main() { 
      foo(22);
    }
    

    This should not lint, because at present '_ does not work in that position (which we should fix).

    Niko Matsakis at 2017-10-31 19:47:50

  9. OK, let's start with this specific test:

    fn deref<'x>(v: &'x u32) -> u32 {
        *v
    }
    
    fn main() { }
    

    When we are done, we want to issue a warning, probably like this:

    fn deref<'x>(v: &'x u32) -> u32 {
    //       ^^ lifetime name `'x` only used once
        *v
    }
    
    fn main() { }
    

    In this case, the one use of 'x is as the operand to a &-type, so I think we don't have to say much more than that. If the one use of 'x were in a struct, we might want to have a hint indicating that it can be replaced with '_, but let's leave that stuff to future work.

    The first thing we want to do then is to figure out how many times each name is used and where. Now, accounting around lifetimes (e.g., early-bound, late-bound, etc) can get kind of complicated, but luckily we can ignore most of that crap for our purposes. I imagine we would want to add to the LifetimeContext struct a new field:

    lifetime_uses: DefIdMap<LifetimeUseSet<'tcx>
    

    where a LifetimeUseSet<'tcx> is defined like:

    enum LifetimeUseSet<'tcx> {
        One(&'tcx hir::Lifetime),
        Many,
    }
    

    We don't really need to track more detail than that -- once there are many uses of a lifetime, we don't want to warn about it anymore, so we don't need to track them. Now, when we are resolving a lifetime in resolve_lifetime_ref, we want to update these lifetime use sets. Actually, I think the place to add code is the insert_lifetime method, which records a successful lifetime resolution:

    https://github.com/rust-lang/rust/blob/3cf28f3002470f61a471c69b869e4f55ae1766f7/src/librustc/middle/resolve_lifetime.rs#L1516-L1518

    Here, we want to match on the def, which is of the type Region. We want to do something like:

    match def {
        LateBoundAnon(..) | Static => {
            // These are anonymous lifetimes or lifetimes that are not declared.
        }
    
        Free(_, def_id) | LateBound(_, def_id) | EarlyBound(_, def_id) => {
            // A lifetime declared by the user.
            if !self.lifetime_uses.contains_key(&def_id) {
                self.lifetime_uses.insert(def_id, LifetimeUseSet::One(lifetime_ref));
            } else {
                self.lifetime_uses.insert(def_id, LifetimeUseSet::Many);
            }
        }
    }
    

    Now we have a record of where each lifetime def was used. The next step will be going over the set of lifetime names defined and warning if they are LifetimeUseSet::One. I'm going to stop here, but we can talk about the best way to do that later.

    Niko Matsakis at 2017-11-11 19:20:40

  10. I've been in touch with @nikomatsakis over this. The current status is: I've followed the instructions he has given above and written code for the same locally.

    Gauri Kholkar at 2017-11-13 14:11:58

  11. So, if we follow the steps above, we wind up with a map from the def-id of each LifetimeDef to either zero (no key), one, or many uses of it. I think maybe the easiest thing way to handle things is to do a second walk of the krate, just looking for lifetime defs, and issuing the warnings. Originally I had thought we could do them as we walk the first time, but now that seems like it would just clutter the code unnecessary.

    This is the main function for resolve_lifetime:

    https://github.com/rust-lang/rust/blob/master/src/librustc/middle/resolve_lifetime.rs#L259-L285

    I am imagining that we can basically write a second visitor, struct LifetimeSingleUseWarnVisitor or something. Like LintContext, it will implement the visitor trait and return All for its nested_visitor_map:

    https://github.com/rust-lang/rust/blob/master/src/librustc/middle/resolve_lifetime.rs#L287-L290

    It would only implement one method, though, visit_lifetime_def:

    fn visit_lifetime_def(&mut self, lifetime_def: &hir::LifetimeDef) {
        let def_id = self.tcx.hir.as_local_def_id(lifetime_def.id);
        ...
    }
    

    In there, it will check if the map contains def_id. If not, it can issue a warning "lifetime never used". If so, and it contains One, it will issue a warning "lifetime used only once". Otherwise, no warning. (Issuing a warning is done by calling add_lint, you can grep around for example.)

    OK, this is a not quite right -- we're going to eventually want to be a bit more selective, since it will depend whether that use is one that could be elided or replaced with '_. But let's start there and then narrow it down.

    Niko Matsakis at 2017-11-13 19:38:39

  12. Actually I take that back. Don't use a visitor. Just iterate over the map we built and look things up in the HIR map. For example, given the DefId of some lifetime def, we can look up its span in the HIR map by doing

    let node_id = hir.as_local_node_id(def_id).unwrap(); // guarnateed to be local
    let hir_lifetime: &hir::Lifetime = match hir.get(node_id) {
        hir_map::NodeLifetime(l) => l,
        _ => bug!()
    };
    let span = hir_lifetime.span;
    

    Niko Matsakis at 2017-11-13 20:26:51

  13. Well, that won't allow you to catch lifetimes used zero times. To do that, we either need a separate visitor, or visit the map during with and store a "zero times" entry (extending the enum), or to visit the entries at the end of with and issue warnings.

    Niko Matsakis at 2017-11-13 20:29:44

  14. Track the progress here

    Gauri Kholkar at 2017-11-23 20:03:36

  15.   |
    3 | fn deref<'x>(v: &'x u32) -> u32 {
      |          ^^
      |
    note: lint level defined here
     --> /home/user/mp/foo.rs:1:9
      |
    1 | #![warn(single_use_lifetime)]
      |         ^^^^^^^^^^^^^^^^^^^
    

    Is what gets generated for now

    Gauri Kholkar at 2017-11-25 10:55:36

  16. @nikomatsakis it's compile-fail tests that are failing now

    Gauri Kholkar at 2017-12-08 16:08:41

  17. I started trying to record all places where this lint might be needed and document their expected behavior. This is not done, but I have to go, so I'm saving my status here for now. My goal is to make a checklist and try to mentor the remaining improvements.

    File can be found in this gist.

    Niko Matsakis at 2018-03-13 22:14:40

  18. Here is an updated check-list of the overall goals:

    moved to issue header

    Niko Matsakis at 2018-03-14 19:29:41

  19. Well, that won't allow you to catch lifetimes used zero times

    This isn't implemented either

    Gauri Kholkar at 2018-03-14 19:32:57

  20. Ah, good point. I was wondering if we might want to actually handle the "multiple edits" this way -- that is, we could first suggest removing the 'a from &'a u32, then suggest removing an unused lifetime.

    Niko Matsakis at 2018-03-14 19:36:36

  21. Kind of annoying to have to run rustfix to a fixed point though =)

    Niko Matsakis at 2018-03-14 19:36:45

  22. @gaurikholkar I updated the list of warnings

    Niko Matsakis at 2018-03-14 19:38:16

  23. is it possible to give suggested fixes that make changes to multiple spots at once? I guess that might just be multiple suggestions?

    The suggestions machinery is capable of having multiple suggestions at one time, and each suggestions being different Spans, but there's no method access these features, which means it hasn't been thoroughly tested.


    EDIT

    The support to suggest multiple substitutions at once was added in #50943, and a slight tweak to the presentation will be added in #50987.

    Esteban Kuber at 2018-03-14 20:24:36

  24. Issuing warnings at the right times

    This is the first step for the mentoring instructions. The lint as is is partially implemented, but it often fires at the wrong times.

    Step one: Add the tests. I've created a fairly comprehensive set of test cases in this gist. The first step then would be to add these tests into your branch, by doing something like the following. This will put all of the tests into src/test/ui/in-band-lifetimes/single-use-lint.

    > cd src/test/ui/in-band-lifetimes
    > mkdir single-use-lint
    > git clone git@gist.github.com:f13da812c97b8094a9566ca9b3d9677d.git tmp
    > mv tmp/*rs single-use-lint
    > rm -rf tmp
    

    (Unfortunately, I forgot when making those tests that we already had a fair number of tests with names like src/test/ui/in-band-lifetimes/single_use_lifetimes-*rs. You might just want to remove those tests, or else compare them to the new ones.)

    We can then run all the tests by running this command from the main directory:

    > ./x.py test --stage 1 -i src/test/ui --test-args single-use-lint
    

    That should run just the new tests and nothing else. You should see various failures. (You can learn more about rustc's test suite here, in the rustc-guide; these test are ui tests.)

    Step two: Examine existing code. The way that the current lint works is in the resolve_lifetime pass. Basically, for each lifetime that is declared, we track a LifetimeUseSet indicating how many times it is used:

    https://github.com/rust-lang/rust/blob/c19264fa835a1eca86de4fd2e86a87b3919e57cf/src/librustc/middle/resolve_lifetime.rs#L61-L66

    These are kept in a map, indexed by the DefId from the lifetime definition:

    https://github.com/rust-lang/rust/blob/c19264fa835a1eca86de4fd2e86a87b3919e57cf/src/librustc/middle/resolve_lifetime.rs#L258

    This map is populated in the insert_lifetime routine, which is what we invoke when we have resolved some reference to a lifetime. So e.g. if you have a type &'a u32, this method has the job of recording which lifetime that 'a refers to:

    https://github.com/rust-lang/rust/blob/c19264fa835a1eca86de4fd2e86a87b3919e57cf/src/librustc/middle/resolve_lifetime.rs#L2201

    Towards the bottom you will see this code, which upgrades the number of times that the reference is used:

    https://github.com/rust-lang/rust/blob/c19264fa835a1eca86de4fd2e86a87b3919e57cf/src/librustc/middle/resolve_lifetime.rs#L2228-L2233

    Examine the test failures. Looking at the test failures, we can group them into two categories:

    Making lifetimes used in particular places issue a warning. I think what we want to do here is to extend the LifetimeContext struct with a new field:

    https://github.com/rust-lang/rust/blob/c19264fa835a1eca86de4fd2e86a87b3919e57cf/src/librustc/middle/resolve_lifetime.rs#L228

    Let's call the flag track_individual_lifetime_uses: bool. The idea is that, when this flag is false, insert_lifetime is not going to track whether a lifetime is used once or many times: if it finds any use, it will treat it as though it is used many times. So we might modify the insert_lifetime code as follows:

    if self.track_individual_lifetime_uses && !self.lifetime_uses.contains_key(&def_id) {
        self.lifetime_uses.insert(def_id, LifetimeUseSet::One(lifetime_ref));
    } else {
        self.lifetime_uses.insert(def_id, LifetimeUseSet::Many);
    }
    

    The basic idea is that this field should be true whenever a single-use lifetime could be replaced with '_. This is actually not true most of the time, so we can initialize the field to false. We can then modify the with function, which is the function that adds a new lifetime scope, to update the field:

    https://github.com/rust-lang/rust/blob/c19264fa835a1eca86de4fd2e86a87b3919e57cf/src/librustc/middle/resolve_lifetime.rs#L1247-L1249

    with should take a new argument (track_lifetime_uses: bool); it should save the old value of the flag, modify it, and then restore it before returning.

    Usually, we will want to pass false as the value of this flag, but there are some exceptions. For example, when checking fn arguments:

    https://github.com/rust-lang/rust/blob/c19264fa835a1eca86de4fd2e86a87b3919e57cf/src/librustc/middle/resolve_lifetime.rs#L1627-L1637

    and function bodies:

    https://github.com/rust-lang/rust/blob/c19264fa835a1eca86de4fd2e86a87b3919e57cf/src/librustc/middle/resolve_lifetime.rs#L451-L466

    Anyway, that should be the rough idea. We'll have to see if it starts to issue warnings at strange times!

    Making lifetimes that are never used issue a warning. Warnings are actually issued in this loop:

    https://github.com/rust-lang/rust/blob/c19264fa835a1eca86de4fd2e86a87b3919e57cf/src/librustc/middle/resolve_lifetime.rs#L1273-L1299

    The problem here is that, if a lifetime is never used, it will never be added to the map, so we never see it to issue a warning! To fix that, we have to keep a separate set of all lifetimes and iterate over that set. Otherwise, we could walk the IR one more time with a separate visitor to find each lifetime definition.

    Niko Matsakis at 2018-03-21 14:28:51

  25. @nikomatsakis thanks for the detailed instructions!

    The approach you outline doesn't totally work out, I think. Consider two-uses-in-fn-argument-and-return - we want to permit the lifetime here because it has two uses, but we want to pass false when calling self.with on the output (because lifetimes on the output alone are permitted).

    I think we need to always populate self. lifetime_uses but only check it when track_lifetime_uses is true. What do you think?

    Other than that, to do the last part (finding lifetimes with zero uses): where would you populate the set of lifetimes?

    Tamir Duberstein at 2018-04-08 01:12:41

  26. Also, a few of the tests are incoherent:

    Tamir Duberstein at 2018-04-08 01:42:38

  27. @tamird

    Sorry I missed those comments!

    Regarding the incoherent tests:

    • one-use-in-inherent-method-argument: I believe the comment is wrong. We should warn now, as you could write impl Foo<'_>.
    • one-use-in-inherent-method-return: I think the impl should be changed to impl Foo<'_>, but no error is expected.
    • one-use-in-trait-method-argument: here a warning IS expected, but for 'g, not 'f
    • two-uses-in-inherent-method-argument-and-return: should be changed I think to impl Foo<'_>, and then no warnings would be expected

    Niko Matsakis at 2018-04-12 14:21:15

  28. @tamird

    The approach you outline doesn't totally work out, I think

    Regarding this... I think actually that the two-uses-in-fn-argument-and-return test:

    fn c<'a>(x: &'a u32) -> &'a u32 { // OK: used twice
        &22
    }
    

    is ok for two independent reasons: first, because the lifetime is used twice, but also because it is used in the return type. So it would be ok to invoke with with false (while visiting the return type). Hence tests like one-use-in-inherent-method-return still do not warn.

    Put another way, I do not yet see the problem. =)

    Other than that, to do the last part (finding lifetimes with zero uses): where would you populate the set of lifetimes?

    Hmm. I was thinking about this later. Keeping a set of "all lifetimes" doesn't feel right to me. In fact, the point where we iterate over the lifetimes in order to issue warnings is precisely the point where a set of declared lifetimes are going out of scope:

    https://github.com/rust-lang/rust/blob/c19264fa835a1eca86de4fd2e86a87b3919e57cf/src/librustc/middle/resolve_lifetime.rs#L1273-L1299

    That is, the purpose of the with function is to bring new lifetimes into scope. The lifetimes are found on the scope parameter, which is of type Scope<'a>:

    https://github.com/rust-lang/rust/blob/c19264fa835a1eca86de4fd2e86a87b3919e57cf/src/librustc/middle/resolve_lifetime.rs#L261-L315

    we could iterate over all the lifetimes that were declared in Scope. We basically only care about the case where Scope is a Binder, in which case we can iterate over the lifetimes map:

    https://github.com/rust-lang/rust/blob/c19264fa835a1eca86de4fd2e86a87b3919e57cf/src/librustc/middle/resolve_lifetime.rs#L268

    For each Region value found within, we can extract a DefId (look for the /* lifetime_decl */ comments):

    https://github.com/rust-lang/rust/blob/c19264fa835a1eca86de4fd2e86a87b3919e57cf/src/librustc/middle/resolve_lifetime.rs#L68-L83

    then we can lookup that DefId the lifetime_uses map. If it is not present, that should be zero uses.

    Niko Matsakis at 2018-04-12 17:31:21

  29. @tamird -- do you think you are going to try and tackle this issue? That would be great! (If not, though, let me know so I can find someone else; I'd like to see this get done.)

    Niko Matsakis at 2018-04-12 17:31:56

  30. @nikomatsakis yep, I'm looking at this. Is there any reason to keep lifetime_uses? it seems cleaner to combine the usage information into the binder's lifetimes map.

    Tamir Duberstein at 2018-04-19 10:01:49

  31. @tamird

    Is there any reason to keep lifetime_uses?

    If you think you see a cleaner way, go for it!

    Niko Matsakis at 2018-04-25 14:27:36

  32. @tamird btw, just checking -- how goes? I was away last week for PTO

    Niko Matsakis at 2018-04-25 14:36:41

  33. ping @tamird -- not trying to bug ya', just want to know if you're still working on this. I'm doing my weekly sweep and trying to figure out what I should do to ensure that this moves forward. It's .. mildly high priority, mostly because we'd like to encourage people to try out the in-band lifetimes feature, and for them to do that effectively, this lint ought to be a big help...

    Niko Matsakis at 2018-04-30 18:26:14

  34. First PR: https://github.com/rust-lang/rust/pull/50440

    (I checked with @tamird and they would not be able to get to this for some time yet)

    Niko Matsakis at 2018-05-04 09:56:00

  35. Given that the 2018 edition preview included in-band lifetimes, it seems like we need to have this lint available for either the next preview or the 2018 edition stable release.

    Josh Triplett at 2018-06-24 18:02:34

  36. Even without in-band lifetimes, I think this is an important idiom lint, so I've added it to an edition milestone to get eyes on it. Edition folks: please move or remove if you think otherwise.

    scottmcm at 2018-08-15 03:57:05

  37. Wow, it's been three years...

    I still think this is important. I started looking at how in_band_lifetimes in rustc_mir_transform, and I immediately found a bunch of examples of unnecessary 'as. Here's just in coverage/spans.rs, since it was at the bottom of the error list:

    -    pub fn format(&self, tcx: TyCtxt<'tcx>, mir_body: &'a mir::Body<'tcx>) -> String {
    +    pub fn format<'tcx>(&self, tcx: TyCtxt<'tcx>, mir_body: &mir::Body<'tcx>) -> String {
    
    -    pub fn format(&self, tcx: TyCtxt<'tcx>, mir_body: &'a mir::Body<'tcx>) -> String {
    +    pub fn format<'tcx>(&self, tcx: TyCtxt<'tcx>, mir_body: &mir::Body<'tcx>) -> String {
    
    -    pub fn format_coverage_statements(
    +    pub fn format_coverage_statements<'tcx>(
             &self,
             tcx: TyCtxt<'tcx>,
    -        mir_body: &'a mir::Body<'tcx>,
    +        mir_body: &mir::Body<'tcx>,
         ) -> String {
    
    -pub(super) fn filtered_statement_span(statement: &'a Statement<'tcx>) -> Option<Span> {
    +pub(super) fn filtered_statement_span<'tcx>(statement: &Statement<'tcx>) -> Option<Span> {
    
    -pub(super) fn filtered_terminator_span(terminator: &'a Terminator<'tcx>) -> Option<Span> {
    +pub(super) fn filtered_terminator_span<'tcx>(terminator: &Terminator<'tcx>) -> Option<Span> {
    

    scottmcm at 2021-12-06 04:57:05

  38. Okay, I've created the F-lint-single_use_lifetiems label and tagged the 6 relevant issues open for this.

    The examples in the "current status" section at the top all warn and have a suggestion, except for the unused lifetime, which falls under the unused_lifetime lint.

    Marking S-tracking-impl-complete, given that there are several linked issues that need to be fixed.

    Jack Huey at 2022-03-18 17:10:44

  39. With #96833 merged, there has been progress on this issue recently. The number of false positives is now way down, but there's still one remaining, according to the PR's description.

    est31 at 2022-05-29 21:38:18

  40. @est31 what is the remaining false positive?

    Camille Gillot at 2022-06-11 09:04:36

  41. @cjgillot I was referring to:

    Remaining false positive: single-use lifetimes in argument-position impl-trait. I'm waiting for #96529 to be fixed to have a clean and proper solution here.

    Or is the false positive fixed now?

    est31 at 2022-06-11 11:56:22

  42. Indeed, the remaining false positive is:

    trait Foo<'a> {}
    
    #[warn(single_use_lifetimes)]
    fn foo<'a>(_: impl Foo<'a>) {}
    

    Where the lifetime cannot be elided. There is an inconsistency here between plain functions and async functions (which allow elision). I suggested to allow elision in https://github.com/rust-lang/rust/pull/97720.

    If that change is accepted, there won't be a false positive any more, and the suggestion will be correct.

    Camille Gillot at 2022-06-12 12:17:26

  43. I think the following might be an unaddressed false positive:

        pub fn get_json<T>(&self, limit: u64) -> Result<(Meta, T)>
        where
            for<'de> T: Deserialize<'de>,
    
    error: lifetime parameter `'de` only used once
       --> crates/client/src/entity.rs:182:13
        |
    182 |         for<'de> T: Deserialize<'de>,
        |             ^^^                 --- ...is used only here
        |             |
        |             this lifetime...
    

    I'm not aware of any way to use anonymous lifetimes to avoid the single-use lifetime here.

    bstrie at 2022-08-09 14:26:15

  44. Another false positive:

    fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result<Mime, D::Error> {
    
    error: lifetime parameter `'de` only used once
      --> crates/type/src/meta.rs:31:16
       |
    31 | fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result<Mime, D::Error> {
       |                ^^^ this lifetime... --- ...is used only here
    

    Are anonymous lifetimes supposed to work here? The error:

    error[E0637]: `'_` cannot be used here
      --> crates/type/src/meta.rs:31:32
       |
    31 | fn deserialize<D: Deserializer<'_>>(deserializer: D) -> Result<Mime, D::Error> {
       |                                ^^ `'_` is a reserved lifetime name
    

    Similar example:

        fn decode<'i, I>(values: &mut I) -> Result<Self, HeadErr>
        where
            Self: Sized,
            I: Iterator<Item = &'i HeaderValue>,
    

    bstrie at 2022-08-09 14:44:01

  45. @bstrie, those cases are correctly handled by beta 1.63.0 https://play.rust-lang.org/?version=beta&mode=debug&edition=2021&gist=4fd54ae5a4d4a3a936054787f6655b7b

    Camille Gillot at 2022-08-09 16:22:42

  46. This appears to ignore the fact that lifetimes inside impl Trait are currently unstable. See:

    #![warn(single_use_lifetimes)]
    fn test<'a>(val: &(impl 'a + Iterator)) {}
    

    This actually suggests removing the lifetime, which in turn is suggested to be added back with:

    #![warn(single_use_lifetimes)]
    fn test(val: &(impl '_ + Iterator)) {}
    

    Clar Fon at 2023-09-08 18:43:56

  47. Summarizing the current state of this:

    • https://github.com/rust-lang/rust/pull/97720 was merged, but it requires the unstable feature anonymous_lifetime_in_impl_trait
    • anonymous_lifetime_in_impl_trait doesn't have a tracking issue, but https://github.com/rust-lang/rust/pull/107378 proposes stabilization of it.
    • https://github.com/rust-lang/rust/pull/107378 is blocked on a concern about interaction with GATs. There are some suggestions to reduce the scope of what's being stabilized, but I wasn't clear on the state of that.
    • anonymous_lifetime_in_impl_trait also has an ICE: https://github.com/rust-lang/rust/issues/124340

    I think to make forward progress on this:

    • We need a fix for the ICE, and
    • Either:
      • Lang needs to discuss the GAT issue and reach a consensus, or
      • The stabilization scope needs to reduce to not include that, and lang needs to come to a consensus on the reduced-scope stabilization

    Josh Triplett at 2024-08-26 22:21:04