Lifetime issues caused by implicitly choosing the wrong implementation of Borrow are incomprehensible
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 119k
- Forks
- 16.1k
- PR merge metrics
- PR metrics pending
Description
Code
use std::collections::BTreeSet;
pub fn find_static<'a>(map: &BTreeSet<&'static str>, key: &'a str) -> &'static str {
*map.get(&key).unwrap()
}
Current output
error: lifetime may not live long enough
--> src/lib.rs:4:5
|
3 | pub fn find_static<'a>(map: &BTreeSet<&'static str>, key: &'a str) -> &'static str {
| -- lifetime `'a` defined here
4 | *map.get(&key).unwrap()
| ^^^^^^^^^^^^^^^^^^^^^^^ returning this value requires that `'a` must outlive `'static`
Desired output
error: lifetime may not live long enough
--> src/lib.rs:4:5
|
3 | pub fn find_static<'a>(map: &BTreeSet<&'static str>, key: &'a str) -> &'static str {
| -- lifetime `'a` defined here
4 | *map.get(&key).unwrap()
| ^^^^^^^^^^^^^^^^^^^^^^^ returning this value requires that `'a` must outlive `'static`
note: `'static` in the type of `map` narrowed to `'a` to match `key`
note: lifetimes must match due to the implementation of `Borrow` being used
--> rustc-src/std/blah/borrow.rs:12345
impl<T> Borrow<T> for T { ... }
^^^^^^^^^^^^^^^^^^^^^^^
help: consider removing the borrow here
|
4 | *map.get(&key).unwrap()
| -
| ( key)
Rationale and extra context
This is a very confusing case that definitely needs more attention in the diagnostics in order to be understandable by a typical Rust developer. After much head-scratching, I have arrived at the following explanation of why this works like that:
- when doing
get(key), the trait bounds require&'static str: Borrow<str>, which resolves throughimpl<T> Borrow<T> for &Tand all is good with the world - when doing
get(&key), the trait bounds require&'static str: Borrow<&'a str>, which resolves throughimpl<T> Borrow<T> for Tand unifies'staticwith'a, which means that the compiler uses variance to shrink the'staticin&BTreeSet<&'static str>to&BTreeSet<&'a str>
Other cases
If the key is a lifetime-parametrized struct, there is no easy way of achieving the same thing:
use std::collections::BTreeMap;
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
struct Meow<'a> {
pub name: &'a str,
pub index: usize,
}
pub fn launder_lifetime<'a>(map: &BTreeMap<Meow<'static>, usize>, key: Meow<'a>) -> Meow<'static> {
// same error as above
*map.get_key_value(&key).unwrap().0
}
The best way of making this compile that I know of involves this monstrosity:
use std::borrow::Borrow;
use std::collections::BTreeMap;
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
struct Meow<'a> {
pub name: &'a str,
pub index: usize,
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
struct UglyWorkaround<'a>(Meow<'a>);
impl<'a, 'b: 'a> Borrow<Meow<'a>> for UglyWorkaround<'b> {
fn borrow(&self) -> &Meow<'a> {
&self.0
}
}
pub fn launder_lifetime<'a>(map: &BTreeMap<UglyWorkaround<'static>, usize>, key: Meow<'a>) -> Meow<'static> {
map.get_key_value(&key).unwrap().0.0
}
Guiding the user towards that would probably have to involve an example in the docs...
Rust Version
rustc 1.86.0-nightly (43ca9d18e 2025-02-08)
binary: rustc
commit-hash: 43ca9d18e333797f0aa3b525501a7cec8d61a96b
commit-date: 2025-02-08
host: x86_64-unknown-linux-gnu
release: 1.86.0-nightly
LLVM version: 19.1.7
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Reproduce the provided BTreeSet and BTreeMap examples with the stated Rust nightly version, then trace the compiler diagnostic path for the lifetime error and the selected Borrow implementation. Compare the current output with the requested notes and help; done means the diagnostics explain the implicit Borrow choice and offer an appropriate correction, with regression coverage for the examples.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- compilers
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 25/100