I have a data structure like this:
struct R {
hmhs: HashMap<i64, HashSet<i64>>,
}
impl R {
fn hs_for_hmhs(&mut self) -> &mut HashSet<i64> {
if let None = self.hmhs.get(&0) {
self.hmhs.insert(0, HashSet::new());
}
self.hmhs.get_mut(&0).unwrap()
}
fn iter_for_hmhs<'a>(&'a mut self) -> impl Iterator<Item = &'a i64> {
self.hs_for_hmhs().iter()
}
fn insert_for_hmhs(&mut self, i: i64) -> bool {
self.hs_for_hmhs().insert(i)
}
}
This seems to work, but all the methods require a mutable reference to self
which is unfortunate. I tried to give interior mutability a go:
struct S {
hmhs: RefCell<HashMap<i64, HashSet<i64>>>,
}
impl S {
fn hs_for_hmhs(&self) -> &HashSet<i64> {
if let None = self.hmhs.borrow().get(&0) {
self.hmhs.borrow_mut().insert(0, HashSet::new());
}
self.hmhs.borrow_mut().get_mut(&0).unwrap()
}
fn iter_for_hmhs(&mut self) -> impl Iterator<Item = &i64> {
self.hs_for_hmhs().iter()
}
fn insert_for_hmhs(&mut self, i: i64) -> bool {
self.hs_for_hmhs().insert(i)
}
}
However, I constantly seem to hit problems. Mostly some variety of How do I return a reference to something inside a RefCell without breaking encapsulation?
I have tried lots of variants here, but I am missing something fundamental in my understanding. Is there a way of achieving what I want?
use std::cell::RefCell;
use std::collections::{HashMap, HashSet};
struct R {
hmhs: HashMap<i64, HashSet<i64>>,
}
impl R {
fn hs_for_hmhs(&mut self) -> &mut HashSet<i64> {
if let None = self.hmhs.get(&0) {
self.hmhs.insert(0, HashSet::new());
}
self.hmhs.get_mut(&0).unwrap()
}
fn iter_for_hmhs<'a>(&'a mut self) -> impl Iterator<Item = &'a i64> {
self.hs_for_hmhs().iter()
}
fn insert_for_hmhs(&mut self, i: i64) -> bool {
self.hs_for_hmhs().insert(i)
}
}
struct S {
hmhs: RefCell<HashMap<i64, HashSet<i64>>>,
}
impl S {
fn hs_for_hmhs(&self) -> &mut HashSet<i64> {
if let None = self.hmhs.borrow().get(&0) {
self.hmhs.borrow_mut().insert(0, HashSet::new());
}
self.hmhs.borrow_mut().get_mut(&0).unwrap()
}
fn iter_for_hmhs(&self) -> impl Iterator<Item = &i64> {
self.hs_for_hmhs().iter()
}
fn insert_for_hmhs(&self, i: i64) -> bool {
self.hs_for_hmhs().insert(i)
}
}
fn main() {}
Compiler Message:
error[E0597]: borrowed value does not live long enough
--> src/main.rs:36:9
|
36 | self.hmhs.borrow_mut().get_mut(&0).unwrap()
| ^^^^^^^^^^^^^^^^^^^^^^ temporary value does not live long enough
37 | }
| - temporary value only lives until here
|
note: borrowed value must be valid for the anonymous lifetime #1 defined on the method body at 31:5...
--> src/main.rs:31:5
|
31 | / fn hs_for_hmhs(&self) -> &mut HashSet<i64> {
32 | | if let None = self.hmhs.borrow().get(&0) {
33 | | self.hmhs.borrow_mut().insert(0, HashSet::new());
34 | | }
35 | |
36 | | self.hmhs.borrow_mut().get_mut(&0).unwrap()
37 | | }
| |_____^
I found a solution -- extract the HashMap
as a raw pointer. This in turn means that I can get to the HashSet
without shenanigans including returning a iterator.
I'm happy enough with this as a solution. The unsafe code is small and contained and if I understand the reason why the compiler is complaining without unsafe, it cannot occur in this code, since neither the HashMap
nor the HashSet
are ever removed or replaced after construction.
That was a lot of effort.
use std::cell::RefCell;
use std::collections::{HashMap, HashSet};
struct R {
hmhs: HashMap<i64, HashSet<i64>>,
}
impl R {
fn hs_for_hmhs(&mut self) -> &mut HashSet<i64> {
if let None = self.hmhs.get(&0) {
self.hmhs.insert(0, HashSet::new());
}
self.hmhs.get_mut(&0).unwrap()
}
fn iter_for_hmhs<'a>(&'a mut self) -> impl Iterator<Item = &'a i64> {
self.hs_for_hmhs().iter()
}
fn insert_for_hmhs(&mut self, i: i64) -> bool {
self.hs_for_hmhs().insert(i)
}
}
struct S {
hmhs: RefCell<HashMap<i64, HashSet<i64>>>,
}
impl S {
fn hs_as_ptr(&self) -> *mut HashMap<i64, HashSet<i64>> {
self.hmhs.borrow_mut().entry(0).or_insert(HashSet::new());
self.hmhs.as_ptr()
}
fn mut_hs_for_hmhs(&mut self) -> &mut HashSet<i64> {
unsafe { (*self.hs_as_ptr()).get_mut(&0).unwrap() }
}
fn hs_for_hmhs(&self) -> &HashSet<i64> {
unsafe { (*self.hs_as_ptr()).get(&0).unwrap() }
}
fn iter_for_hmhs<'a>(&'a self) -> impl Iterator<Item = &'a i64> + 'a {
self.hs_for_hmhs().iter()
}
fn insert_for_hmhs(&mut self, i: i64) -> bool {
self.mut_hs_for_hmhs().insert(i)
}
}
fn main() {
let mut r = R {
hmhs: HashMap::new(),
};
let mut s = S {
hmhs: RefCell::new(HashMap::new()),
};
r.insert_for_hmhs(10);
s.insert_for_hmhs(20);
println!("r next: {:?}", r.iter_for_hmhs().next());
println!("s next: {:?}", s.iter_for_hmhs().next());
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.