如何在不烦扰借用检查器的情况下为双向地图建模?
How can I model a bidirectional map without annoying the borrow checker?
从 我了解到我不能在同一结构中存储值和引用。
建议的解决方案是:
The easiest and most recommended solution is to not attempt to put these items in the same structure together. By doing this, your structure nesting will mimic the lifetimes of your code.
Place types that own data into a structure together and then provide methods that allow you to get references or objects containing references as needed.
但是,我不知道如何在我的具体案例中应用它:
我想构建双向地图,由两个内部 HashMap
实现。
显然,其中之一必须拥有数据。但是,另一部分对于双向地图也是必不可少的,所以我不知道如何在保持双向地图界面的同时将这两者分开。
struct BidiMap<'a, S: 'a, T: 'a> { ? }
fn put(&mut self, s: S, t: T) -> ()
fn get(&self, s: &S) -> T
fn get_reverse(&self, t: &T) -> S
在这种情况下,最简单的解决方案是像具有垃圾收集器的语言一样工作:
use std::collections::HashMap;
use std::rc::Rc;
use std::hash::Hash;
use std::ops::Deref;
struct BidiMap<A, B> {
left_to_right: HashMap<Rc<A>, Rc<B>>,
right_to_left: HashMap<Rc<B>, Rc<A>>,
}
impl<A, B> BidiMap<A, B>
where
A: Eq + Hash,
B: Eq + Hash,
{
fn new() -> Self {
BidiMap {
left_to_right: HashMap::new(),
right_to_left: HashMap::new(),
}
}
fn put(&mut self, a: A, b: B) {
let a = Rc::new(a);
let b = Rc::new(b);
self.left_to_right.insert(a.clone(), b.clone());
self.right_to_left.insert(b, a);
}
fn get(&self, a: &A) -> Option<&B> {
self.left_to_right.get(a).map(Deref::deref)
}
fn get_reverse(&self, b: &B) -> Option<&A> {
self.right_to_left.get(b).map(Deref::deref)
}
}
fn main() {
let mut map = BidiMap::new();
map.put(1, 2);
println!("{:?}", map.get(&1));
println!("{:?}", map.get_reverse(&2));
}
当然,您会想要 多 更严格的代码,因为这可以让您打破双向映射。这只是向您展示了一种解决问题的方法。
Clearly, one of them has to own the data
显然,事实并非如此^_^。在这种情况下,两个映射 共享 使用 Rc
.
的所有权
对这个解决方案进行基准测试 以了解它是否足够高效。
做任何更有效的事情都需要更多地考虑所有权。例如,如果 left_to_right
映射拥有数据,而您在另一个映射中使用了原始指针,则该指针将在第一个映射重新分配后立即失效。
从
建议的解决方案是:
The easiest and most recommended solution is to not attempt to put these items in the same structure together. By doing this, your structure nesting will mimic the lifetimes of your code. Place types that own data into a structure together and then provide methods that allow you to get references or objects containing references as needed.
但是,我不知道如何在我的具体案例中应用它:
我想构建双向地图,由两个内部 HashMap
实现。
显然,其中之一必须拥有数据。但是,另一部分对于双向地图也是必不可少的,所以我不知道如何在保持双向地图界面的同时将这两者分开。
struct BidiMap<'a, S: 'a, T: 'a> { ? }
fn put(&mut self, s: S, t: T) -> ()
fn get(&self, s: &S) -> T
fn get_reverse(&self, t: &T) -> S
在这种情况下,最简单的解决方案是像具有垃圾收集器的语言一样工作:
use std::collections::HashMap;
use std::rc::Rc;
use std::hash::Hash;
use std::ops::Deref;
struct BidiMap<A, B> {
left_to_right: HashMap<Rc<A>, Rc<B>>,
right_to_left: HashMap<Rc<B>, Rc<A>>,
}
impl<A, B> BidiMap<A, B>
where
A: Eq + Hash,
B: Eq + Hash,
{
fn new() -> Self {
BidiMap {
left_to_right: HashMap::new(),
right_to_left: HashMap::new(),
}
}
fn put(&mut self, a: A, b: B) {
let a = Rc::new(a);
let b = Rc::new(b);
self.left_to_right.insert(a.clone(), b.clone());
self.right_to_left.insert(b, a);
}
fn get(&self, a: &A) -> Option<&B> {
self.left_to_right.get(a).map(Deref::deref)
}
fn get_reverse(&self, b: &B) -> Option<&A> {
self.right_to_left.get(b).map(Deref::deref)
}
}
fn main() {
let mut map = BidiMap::new();
map.put(1, 2);
println!("{:?}", map.get(&1));
println!("{:?}", map.get_reverse(&2));
}
当然,您会想要 多 更严格的代码,因为这可以让您打破双向映射。这只是向您展示了一种解决问题的方法。
Clearly, one of them has to own the data
显然,事实并非如此^_^。在这种情况下,两个映射 共享 使用 Rc
.
对这个解决方案进行基准测试 以了解它是否足够高效。
做任何更有效的事情都需要更多地考虑所有权。例如,如果 left_to_right
映射拥有数据,而您在另一个映射中使用了原始指针,则该指针将在第一个映射重新分配后立即失效。