Skip to content

Lifetime of input (key) to HashMap::get() gets entanged w/ that of its output (value) #80389

Open
@zeenix

Description

@zeenix

The following code:

use std::{borrow::Cow, collections::HashMap};

#[derive(Debug)]
struct Message(String);

impl Message {
    fn interface(&self) -> &str {
        &self.0
    }
}

#[derive(Debug)]
struct Proxy<'p>(Cow<'p, str>);

impl<'p> Proxy<'p> {
    fn interface(&self) -> &str {
        &self.0
    }
}

#[derive(Hash, Eq, PartialEq)]
struct ProxyKey<'key>(Cow<'key, str>);

impl From<&Proxy<'_>> for ProxyKey<'_> {
    fn from(proxy: &Proxy<'_>) -> Self {
        ProxyKey(Cow::from(proxy.interface().to_owned()))
    }
}

impl<'key> From<&'key Message> for ProxyKey<'key> {
    fn from(msg: &'key Message) -> Self {
        ProxyKey(Cow::from(msg.interface()))
    }
}

struct ProxyGroup<'p> {
    proxies: HashMap<ProxyKey<'static>, Proxy<'p>>,
}

impl<'p> ProxyGroup<'p> {
    pub fn new() -> Self {
        Self {
            proxies: HashMap::new(),
        }
    }

    pub fn add<'a: 'p>(&mut self, proxy: Proxy<'a>) {
        let key = ProxyKey::from(&proxy);
        self.proxies.insert(key, proxy);
    }

    pub fn handle_next_signal(&self) {
        let msg = Message(String::from("some.interface"));

        match self.get_proxy_for_msg(&msg) {
            Some(_) => Self::consume_msg(msg),,
            None => (),
        }
    }

    fn get_proxy_for_msg<'a, 'b>(&'a self, msg: &'b Message) -> Option<&'a Proxy<'p>> 
    where
        'p: 'a,
    {
        let key = ProxyKey::from(msg);

        self.proxies.get(&key)
    }

    fn consume_msg(_msg: Message) {}
}

Which I (and devs I consulted on the Discord and IRC) think should compile. However, it doesn't and you get the following error:

error[E0623]: lifetime mismatch
  --> src/main.rs:67:9
   |
61 |     fn get_proxy_for_msg<'a, 'b>(&'a self, msg: &'b Message) -> Option<&'a Proxy<'p>> 
   |                                  --------       ----------- these two types are declared with different lifetimes...
...
67 |         self.proxies.get(&key)
   |         ^^^^^^^^^^^^^^^^^^^^^^ ...but data from `msg` flows into `self` here

error: aborting due to previous error

i-e for some reason the lifetime of the key get associated with lifetime of the Proxy value we get from the HashMap::get call. I think it's likely a compiler bug. Even it is not, it'd be great to get better diagnostics/error message from the compiler for this so I can tell what exactly is going wrong here.

Here is the playground link for trying this out conveniently.

NB: If I change the code to make use of iterator, it works:

    fn get_proxy_for_msg<'a, 'b>(&'a self, msg: &'b Message) -> Option<&'a Proxy<'p>>
    where
        'p: 'a,
    {
        let key = ProxyKey::from(msg);

        self.proxies
            .iter()
            .find(|&(k, _)| &key == k)
            .map(|(_, v)| v)
    }

Playground link.

Meta

rustc --version --verbose:

rustc 1.48.0 (7eac88abb 2020-11-16)
binary: rustc
commit-hash: 7eac88abb2e57e752f3302f02be5f3ce3d7adfb4
commit-date: 2020-11-16
host: x86_64-unknown-linux-gnu
release: 1.48.0
LLVM version: 11.0

Reproducible on nightly (bb17823 2020-12-25) as well.

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-collectionsArea: `std::collections`A-lifetimesArea: Lifetimes / regionsC-bugCategory: This is a bug.T-libs-apiRelevant to the library API 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