Trying to return reference from RwLock, “borrowed value does not live long enough” Error

我是研究僧i 提交于 2019-12-01 07:13:29

问题


I've been working on my first Rust project recently but have hit a snag. I am using a HashMap mapping Strings to AtomicUsize integers. The HashMap is protected by a RwLock to allow for concurrent access. I would like to be able to return references to AtomicUsize values in the HashMap, however if I try to return these references to the caller past the lifetime of the RwLockWriteGuard I get an error that borrowed value does not live long enough. I've reproduced a minimal example below and put the same example on the Rust playground here.

use std::collections::HashMap;
use std::sync::RwLock;
use std::sync::atomic::{AtomicUsize, Ordering};

struct Bar {
    val: AtomicUsize
}

impl Bar {
    pub fn new() -> Self {
        Bar { val: AtomicUsize::new(0) }
    }
}

struct Foo {
    map: RwLock<HashMap<String, Bar>>
}


impl Foo {
    pub fn get(&self, key: String) -> &Bar {
        self.map.write().unwrap().entry(key).or_insert(Bar::new())
    }
}

fn main() {
    let foo = Foo {map: RwLock::new(HashMap::new())};
    let bar = foo.get("key".to_string());
}

The error I get occurs on the line:

self.map.write().unwrap().entry(key).or_insert(Bar::new())

And is because the borrowed value does not live long enough. I've read a few other posts that discuss this error, this one in particular was especially relevant. After reading it over, I can gather that a value returned from a mutex must have a lifetime less than that of the mutex, which would seem to rule out completely what I'm trying to do. I can see why this should be impossible, because if we have a pointer into the Hashmap and another inserts values into the mutex which cause it to be resized, then we will have a dangling pointer.

My question, then, is twofold. Firstly, I'm just curious if I understand the problem correctly or if there is another reason why I'm disallowed from doing what I tried to do? And my second question is if there is perhaps another way to accomplish what I am trying to do without Box the atomic integers and storing those in the HashMap? Such an approach seems like it should work to me because we can return a pointer to the Boxed value which would always be valid. However it seems like this approach would be inefficient because it would require an extra layer of pointer indirection and an extra allocation. Thanks!


回答1:


You're correct that you can't return a reference to something which outlives the MutexGuard, because that would lead to a possibly dangling pointer.

Wrapping the contents in a Box won't help, though! A Box is an owned pointer and apart from the redirection behaves like the contained value as far as reference lifetime goes. After all, if you returned a reference to it, someone else might remove it from the HashMap and de-allocate it.

Depending on what you want to do with the reference, I can think of a couple of options:

  1. Instead of Boxing the values, wrap them in Arc. You would clone the Arc when taking from the HashMap, and multiple references can live at the same time.

  2. You could also return the MutexGuard along with the reference; see this question, which would work well if you just want to operate on the value and then drop the reference relatively soon. This would keep the mutex held until you're finished with it.



来源:https://stackoverflow.com/questions/40299671/trying-to-return-reference-from-rwlock-borrowed-value-does-not-live-long-enoug

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!