我有一个类型,它的数据存储在一个被封装在 Rc<RefCell<>>
中的容器中,这个容器在公共 API 中大部分都是隐藏的。例如:
struct Value;
struct Container {
storage: Rc<RefCell<HashMap<u32, Value>>>,
}
impl Container {
fn insert(&mut self, key: u32, value: Value) {
self.storage.borrow_mut().insert(key, value);
}
fn remove(&mut self, key: u32) -> Option<Value> {
self.storage.borrow_mut().remove(&key)
}
// ...
}
然而,要查看容器内部需要返回一个Ref
。这可以使用Ref::map()
来实现,例如:
// peek value under key, panicking if not present
fn peek_assert(&self, key: u32) -> Ref<'_, Value> {
Ref::map(self.storage.borrow(), |storage| storage.get(&key).unwrap())
}
但是,我想要一个不会引起恐慌的版本的 peek
,它将返回 Option<Ref<'_, Value>>
。这是一个问题,因为 Ref::map
要求你返回一个存在于 RefCell
内部的引用,所以即使我想返回 Ref<'_, Option<Value>>
,也行不通,因为 storage.get()
返回的选项是短暂的。
试图使用 Ref::map
从先前查找的键创建一个 Ref
也无法编译:
// doesn't compile apparently the borrow checker doesn't understand that `v`
// won't outlive `_storage`.
fn peek(&self, key: u32) -> Option<Ref<'_, Value>> {
let storage = self.storage.borrow();
if let Some(v) = storage.get(&key) {
Some(Ref::map(storage, |_storage| v))
} else {
None
}
}
可行的方法是进行两次查找,但我真的希望避免这样做:
// works, but does lookup 2x
fn peek(&self, key: u32) -> Option<Ref<'_, Value>> {
if self.storage.borrow().get(&key).is_some() {
Some(Ref::map(self.storage.borrow(), |storage| {
storage.get(&key).unwrap()
}))
} else {
None
}
}
在playground中有可编译的示例。
与这个问题类似的问题假设内部引用始终可用,因此它们没有这个问题。
我发现了Ref::filter_map()
,它可以解决这个问题,但它尚未在稳定版中提供,并且目前不清楚它离稳定还有多远。如果其他选项不可用,我将接受使用unsafe
的解决方案,前提是它是安全的并依赖于文档保证。
v
转换为指针,那么也许我们根本不需要第二个借用,只要我们分两步完成就可以了:1)从映射中检索值并将其转换为指针(因此与借用解除关联),然后调用Ref::map
,其闭包将指针转换回引用:https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=8a199290db5edef6c8c917e4b8898844 - user4815162342