Open
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 &T
and 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 T
and unifies'static
with'a
, which means that the compiler uses variance to shrink the'static
in&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