HashMap和Vec之间的str的共享所有权

时间:2017-02-17 10:57:24

标签: hashmap rust

我来自Java / C#/ JavaScript背景,我正在尝试实现一个Dictionary,它会为每个传递的字符串分配一个永不改变的id。字典应该能够按指定的id返回一个字符串。这允许在文件系统中更有效地存储具有大量重复字符串的一些数据,因为只存储字符串的id而不是整个字符串。

我认为使用HashMapVec的结构会有效,但事实证明它比这更复杂。

我开始使用&str作为HashMap的密钥,使用Vec项,如下例所示。 HashMap的值用作Vec的索引。

pub struct Dictionary<'a> {
    values_map: HashMap<&'a str, u32>,
    keys_map: Vec<&'a str>
}

impl<'a> Dictionary<'a> {
    pub fn put_and_get_key(&mut self, value: &'a str) -> u32 {
        match self.values_map.get_mut(value) {
            None => {
                let id_usize = self.keys_map.len();
                let id = id_usize as u32;
                self.keys_map.push(value);
                self.values_map.insert(value, id);
                id
            },
            Some(&mut id) => id
        }
    }
}

这很好用,直到str s需要存储在某个地方,最好也在同一个struct。我尝试将Box<str>存储在Vec中的&'a strHashMap中。{/ p>

pub struct Dictionary<'a> {
    values_map: HashMap<&'a str, u32>,
    keys_map: Vec<Box<str>>
}

借用检查器当然不允许这样做,因为当HashMap中的项目被删除时,它会允许Vec中的悬空指针(或者实际上有时当另一项添加到Vecunsafe,但这是一个偏离主题的内容。)

我了解到我需要编写Rc代码或使用某种形式的共享所有权,其中最简单的一种似乎是Rc<Box<str>>Rc<str>的用法看起来像引入双重间接,但目前似乎没有简单的方法来构造pub struct Dictionary { values_map: HashMap<Rc<Box<str>>, u32>, keys_map: Vec<Rc<Box<str>>> } impl Dictionary { pub fn put_and_get_key(&mut self, value: &str) -> u32 { match self.values_map.get_mut(value) { None => { let id_usize = self.keys_map.len(); let id = id_usize as u32; let value_to_store = Rc::new(value.to_owned().into_boxed_str()); self.keys_map.push(value_to_store); self.values_map.insert(value_to_store, id); id }, Some(&mut id) => id } } }

HashMap

关于所有权语义,一切似乎都很好,但上面的代码无法编译,因为Rc现在需要&str,而不是error[E0277]: the trait bound `std::rc::Rc<Box<str>>: std::borrow::Borrow<str>` is not satisfied --> src/file_structure/sample_dictionary.rs:14:31 | 14 | match self.values_map.get_mut(value) { | ^^^^^^^ the trait `std::borrow::Borrow<str>` is not implemented for `std::rc::Rc<Box<str>>` | = help: the following implementations were found: = help: <std::rc::Rc<T> as std::borrow::Borrow<T>>

Rc<str>

问题:

  1. 有没有办法构建string-by-id
  2. 哪些其他结构,方法或方法可以帮助解决此问题。基本上,我需要一种方法来有效地存储两个地图id-by-string&str,并且能够通过public void nextButtonAction() throws Exception { FXMLLoader secondScreen = new FXMLLoader(getClass().getResource("/view/SecondScreen.fxml")); Parent root = secondScreen.load(); SecondScreenController secondScreenController = secondScreen.getController(); secondScreenController.setPorts(this.sensorPort, this.gpsPort); Stage stage = (Stage) this.nextButton.getScene().getWindow(); stage.setScene(new Scene(root, 320, 480)); stage.show(); } 检索ID,即没有任何过多的分配。

1 个答案:

答案 0 :(得分:2)

  

有没有办法构建Rc<str>

令人讨厌,不是我所知道的。 Rc::new需要一个Sized参数,我不确定这是一个实际的限制,还是只是被遗忘的东西。

  

哪些其他结构,方法或方法可以帮助解决此问题?

如果您查看get的签名,请注意:

fn get<Q: ?Sized>(&self, k: &Q) -> Option<&V>
    where K: Borrow<Q>, Q: Hash + Eq

因此,如果&str实施K,您可以Borrow<str>进行搜索。

String实现Borrow<str>,因此最简单的解决方案是简单地使用String作为关键字。当然,这意味着你实际上有两个String而不是一个......但它很简单。当然,StringBox<str>更容易使用(尽管它使用了8个字节)。

如果您想削减此费用,可以使用自定义结构:

#[derive(Clone, Debug)]
struct RcStr(Rc<String>);

然后为它实施Borrow<str>。然后,您将为每个密钥分配2个分配(1个用于Rc,1个用于String)。根据{{​​1}}的大小,它可能消耗更少或更多的内存。

如果你想进一步(为什么不呢?),这里有一些想法:

  • 在单个堆分配中实现您自己的引用计数字符串
  • 使用单个竞技场插入String
  • 中插入的切片
  • ...