Skip to content

Lint against creating an invariant, perpetual &mut borrow (&'a mut T<'a>) #120481

Open
@kpreid

Description

@kpreid

Code

pub struct Foo<'a> {
    // `Cow` just used as a convenient lifetime-bearing type
    pub foo: &'a mut std::borrow::Cow<'a, str>
}

Current output

No output

Desired output

error: this mutable reference will borrow its referent forever
 3 | pub foo: &'a mut std::borrow::Cow<'a, str>
   |           --                      -- this use of `'a` requires it to outlive all values of type `std::borrow::Cow<'a, str>`
   |           |
   |           this use of `'a` requires the `std::borrow::Cow<'a, str>` to be exclusively borrowed until the end of `'a`    
   |
   = help: this type is valid, but almost certainly incorrect
   = help: use two distinct lifetime parameters instead: 
              <suggestions to change either the first or second to a newly introduced parameter>
   = note: `#[deny(perpetual_mut_borrow)]` on by default

Rationale and extra context

&'a mut T<'a> is a fairly common beginner mistake. The compiler can even suggest it as an added lifetime annotation if one tries to construct a self-referential struct. It's not at all obvious that this is a mistake (especially as &'a T<'a> is valid and entirely suitable for many applications), and people become quite confused due to the additional, not obviously related, lifetime and drop errors that then result. Therefore, I believe it is worth having the compiler lint this case.

I propose that it be a deny-by-default lint so that

  • it is more prominent than a default warning and indicates “this is so wrong that you need to stop and reassess”, but
  • if someone does have a situation where this is actually the correct lifetime (perhaps they truly intend to make a self-referential struct and use it inside an infinite loop), they can suppress the lint.

Other cases

There may be some subtlety in applying the lint to arbitrary types. My hope is that a simple structural/syntactic rule is sufficiently precise to be helpful: if the same lifetime name appears inside an &mut as the &mut's own lifetime, then lint. But if it is feasible to directly check for "this reference is required to outlive-or-equal its referent" then that would more precisely identify the problem, including accounting for outlives bounds between two lifetimes that have the same effect.

Rust Version

rustc 1.75.0 (82e1608df 2023-12-21)
binary: rustc
commit-hash: 82e1608dfa6e0b5569232559e3d385fea5a93112
commit-date: 2023-12-21
host: x86_64-apple-darwin
release: 1.75.0
LLVM version: 17.0.6

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-lintsArea: Lints (warnings about flaws in source code) such as unused_mut.A-varianceArea: Variance (https://doc.rust-lang.org/nomicon/subtyping.html)C-feature-requestCategory: A feature request, i.e: not implemented / a PR.T-langRelevant to the language team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions