我来自Java / C#/ JavaScript背景,我正在尝试实现一个Dictionary
,它会为每个传递的字符串分配一个永不改变的id。字典应该能够按指定的id返回一个字符串。这允许在文件系统中更有效地存储具有大量重复字符串的一些数据,因为只存储字符串的id而不是整个字符串。
我认为使用HashMap
和Vec
的结构会有效,但事实证明它比这更复杂。
我开始使用&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 str
和HashMap
中。{/ p>
pub struct Dictionary<'a> {
values_map: HashMap<&'a str, u32>,
keys_map: Vec<Box<str>>
}
借用检查器当然不允许这样做,因为当HashMap
中的项目被删除时,它会允许Vec
中的悬空指针(或者实际上有时当另一项添加到Vec
时unsafe
,但这是一个偏离主题的内容。)
我了解到我需要编写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>
问题:
string-by-id
?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,即没有任何过多的分配。答案 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
而不是一个......但它很简单。当然,String
比Box<str>
更容易使用(尽管它使用了8个字节)。
如果您想削减此费用,可以使用自定义结构:
#[derive(Clone, Debug)]
struct RcStr(Rc<String>);
然后为它实施Borrow<str>
。然后,您将为每个密钥分配2个分配(1个用于Rc
,1个用于String
)。根据{{1}}的大小,它可能消耗更少或更多的内存。
如果你想进一步(为什么不呢?),这里有一些想法:
String
,