rust-lang / rust-lang/rust

HashMap struct key with `'static` lifetime screws with return lifetime using methods like `.get()`

Open
#124,614 3 comments 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

A-collections A-lifetimes C-bug T-libs
Dominant language
Rust
Stars
119k
Forks
16.1k
PR merge metrics
PR metrics pending

Description

I'm not sure what words to use to actually describe (or search for a dupe) this issue, so I'll just post it here and hope that it makes more sense to you guys (and sorry if it is a dupe; I'm almost sure it would be)!

So basically, if I write this code:

use std::collections::HashMap;

#[derive(PartialEq,Eq,Hash)]
struct Key<'a> {
    key: &'a str
}

struct Foo {
    types: HashMap<Key<'static>, String>,
}

impl Foo {
    fn resolve<'this, 'a>(
        &'this self,
        key: &'a str,
    ) -> Option<&'this String> {
        self.types.get(&Key { key })
    }
}

I get back this error:

error: lifetime may not live long enough
  --> src/lib.rs:17:9
   |
13 |     fn resolve<'this, 'a>(
   |                -----  -- lifetime `'a` defined here
   |                |
   |                lifetime `'this` defined here
...
17 |         self.types.get(&Key { key })
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ method was supposed to return data with lifetime `'this` but it is returning data with lifetime `'a`
   |
   = help: consider adding the following bound: `'a: 'this`

Despite the fact that HashMap::get() returns the values with a lifetime of self, and nothing to do with the key provided.

However, this does work:

use std::collections::HashMap;

#[derive(PartialEq,Eq,Hash)]
struct Key<'a> {
    key: &'a str
}

struct Foo {
    types: HashMap<&'static str, String>,
}

impl Foo {
    fn resolve<'this, 'a>(
        &'this self,
        key: &'a str,
    ) -> Option<&'this String> {
        self.types.get(key)
    }
}

Removing the struct indirection when defining the key type seems to make things happy!

Background

The above is a minimal reproduction of the issue I ran into, but the reason I ran into it is because I want to have something like:

use std::collections::HashMap;

struct Foo {
    types: HashMap<(String,String), String>,
}

impl Foo {
    fn resolve<'this, 'a>(
        &'this self,
        key1: &'a str,
        key2: &'a str,
    ) -> Option<&'this String> {
        self.types.get(&(key1, key2))
    }
}

In other words, I want to be able to borrow more than 1 value to lookup some owned key in the hashmap. I went down the route of trying struct Key<'a> { a: Cow<'a,str>, b: Cow<'a,str> and then HashMap<Key<'static>, String>, but this led me to bumping up against this odd lifetime issue :)

Any pointers would be amazingly welcome!

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reproducing the minimal example in src/lib.rs, focusing on the HashMap::get call at line 17 and how the borrowed Key lifetime affects the returned value. Compare the behavior with the direct &'static str key example, then determine whether borrow checking or lifetime inference is incorrect and document a confirmed expected result with a regression test.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
compilers
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.