挣扎于内部可变性

时间:2018-09-05 13:18:22

标签: rust borrow-checker mutability

我有这样的数据结构:

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

这似乎可行,但是所有方法都需要可变的 不幸的是参考了self。我试图给 内部可变性:

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

但是,我似乎不断遇到问题。主要是How do I return a reference to something inside a RefCell without breaking encapsulation?

我在这里尝试了很多变体,但是我缺少一些东西 在我的理解中至关重要。有没有办法实现我的目标 要吗?

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

编译器消息:

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 | |     }
   | |_____^

1 个答案:

答案 0 :(得分:0)

我找到了一个解决方案-提取HashMap作为原始指针。反过来,这意味着我可以返回HashSet而无需回头,包括返回迭代器。

对此,我很满意。不安全的代码很小并且包含在内,如果我理解编译器在没有不安全的情况下进行抱怨的原因,那么该代码就不会发生,因为HashMapHashSet都不会在构造后被删除或替换。

那是很大的努力。

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