rust - 如何绕过可变和不可变引用?
问题描述
我正在创建一个库,该库试图成为一个 Cache 结构,该结构包装了一些结构,该结构将是检索“新鲜数据”的组件,例如通过一些 HTTP 请求;还有一个后端字段,它是通过包装组件检索的数据的实际缓存后端。
在检索一些新数据后尝试“刷新”缓存中的数据时,我遇到了一些问题。
use std::collections::HashMap;
pub trait Cacheable<K, V>
where K: std::cmp::Eq + std::hash::Hash
{
fn get(&self, k: &K) -> Option<&V>;
fn set(&mut self, k: K, v: V);
fn del(&mut self, k: &K);
}
pub struct Cache<'a, K, V>
where K: std::cmp::Eq + std::hash::Hash
{
wrapped: &'a dyn Cacheable<K, V>,
backend: Box<dyn Cacheable<K, V>>,
}
impl<'a, K, V> Cache<'a, K, V>
where K: std::cmp::Eq + std::hash::Hash + 'static,
V: 'static,
{
fn New(wrapped: &'a dyn Cacheable<K, V>, backend_type: BackendType) -> Self {
let backend = match backend_type {
BackendType::Memory => MemoryBackend::<K, V>::New(),
};
Cache {
wrapped: wrapped,
backend: Box::new(backend),
}
}
fn get(&mut self, k: &K) -> Option<&V> {
// PROBLEM 1:
// Borrow backend here as immutable
if let Some(v) = self.backend.get(k) {
return Some(v)
}
if let Some(v) = self.wrapped.get(k) {
let cache_k = *k.clone(); // PROBLEM 2: How to clone reference value?
let cache_v = *v.clone(); // PROBLEM 2: How to clone reference value?
// PROBLEM 1:
// Borrow backend here as mutable
self.backend.set(cache_k, cache_v);
return Some(v)
}
None
}
}
pub enum BackendType {
Memory
}
pub struct MemoryBackend<K, V>
where K: std::cmp::Eq + std::hash::Hash
{
data: HashMap<K, V>,
}
impl<K, V> Cacheable<K, V> for MemoryBackend<K, V>
where K: std::cmp::Eq + std::hash::Hash
{
fn get(&self, k: &K) -> Option<&V> {
self.data.get(k)
}
fn set(&mut self, k: K, v: V) {
self.data.insert(k, v);
}
fn del(&mut self, k: &K) {
self.data.remove(k);
}
}
impl<K, V> MemoryBackend<K, V>
where K: std::cmp::Eq + std::hash::Hash
{
fn New() -> Self {
MemoryBackend {
data: HashMap::<K, V>::new(),
}
}
}
问题 1
可变和不可变引用可能共存。这里最好的选择是什么?
我尝试更改get
方法签名以返回 anOption<V>
而不是,Option<&V>
但这也导致我遇到问题 2,我似乎无法克隆共享引用的值。
问题 2
move occurs because value has type `K`, which does not implement the `Copy` trait
help: consider borrowing here: `&*k.clone()`"
AFAIK 我不想将Copy
trait 设置为 bound ofV
因为它可能不是一个简单的类型,并且宁愿克隆它的值而不是将其设置为 API 限制。我似乎无法解决这个问题。
解决方案
-
问题 1:可变引用和不可变引用可能共存。这里最好的选择是什么?我尝试更改
get
方法签名以返回 anOption<V>
而不是,Option<&V>
但这也导致我遇到问题 2,我似乎无法克隆共享引用的值。您可以向 trait 添加一个方法,并使用API
get_or_insert
实现它:MemoryBackend
hash_map::Entry
fn get_or_insert(&mut self, k: K, v: Option<V>) -> Option<&V> { match self.data.entry(k) { Entry::Occupied(entry) => Some(entry.into_mut()), Entry::Vacant(entry) => v.map(|v| &*entry.insert(v)), } }
或者,为了避免评估
v
,除非它是实际需要的(如果上面是内联的,优化过程可能已经做了),你可以传入一个返回的函数Option<V>
:fn get_or_insert_with(&mut self, k: K, f: &dyn Fn() -> Option<V>) -> Option<&V> { match self.data.entry(k) { Entry::Occupied(entry) => Some(entry.into_mut()), Entry::Vacant(entry) => f().map(|v| &*entry.insert(v)), } }
请注意,为了
Cacheable
对象安全,我们必须在此处使用函数特征对象的间接寻址(同样,如果上述内容被内联,则优化传递可能会消除)。 -
问题 2: “移动发生是因为 value 具有 type
K
,它没有实现Copy
trait 帮助:考虑在这里借用:&*k.clone()
” AFAIK 我不想将Copy
trait 设置为 bound ofV
因为它可能不是一个简单的类型,而是更愿意克隆它值而不是将其设置为 API 限制。所以我似乎无法解决这个问题。您可以在实现中添加
K: Clone
和V: Clone
约束:impl<'a, K, V> Cache<'a, K, V> where K: Clone + std::cmp::Eq + std::hash::Hash + 'static, V: Clone + 'static, { // etc }
(游乐场)
推荐阅读
- java - 如何使用 pl*sql developer 运行 .sql 文件?
- html - 选择选项已禁用并已选中 - 仅 CSS
- ionic-framework - IONIC 构建过程太慢
- android - 将一些带有阿拉伯字符串的 TextView 更改为另一种字体阿拉伯风格
- python - jsonify(结果)中的字符串错误 - Flask,SQLAlchemy
- python - Python - 如何将 lambda 函数更改为部分函数?
- node.js - 如何快速发送缓冲区数据?
- sql - 从区间分区中选择最新的分区
- javascript - 我无法输出车把模板中的数据
- android - 来自textview的计时器长时间