简体   繁体   English

挣扎于内部可变性

[英]Struggling with interior mutability

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. 这似乎可行,但是所有方法都需要对self的可变引用,这是不幸的。 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? 大多数情况下, 如何在不破坏封装的情况下返回对RefCell内部内容的引用?

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? 有没有办法实现我想要的?

Complete Code : 完整的代码

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. 我找到了一个解决方案-将HashMap提取为原始指针。 This in turn means that I can get to the HashSet without shenanigans including returning a iterator. 反过来,这意味着我可以使用HashSet而不需要返回返回的迭代器。

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. 不安全的代码很小并且包含在内,如果我理解编译器抱怨不安全的原因,那么在此代码中就不会发生该代码,因为构造后HashMapHashSet都不会被删除或替换。

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());
}

https://play.rust-lang.org/?gist=3ed1977bdd5f9f82d144fe128f618979&version=stable&mode=debug&edition=2015 https://play.rust-lang.org/?gist=3ed1977bdd5f9f82d144fe128f618979&version=stable&mode=debug&edition=2015

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM