【问题标题】:How to get around mutable and immutable references?如何绕过可变和不可变引用?
【发布时间】:2021-12-12 00:13:02
【问题描述】:

我正在创建一个库,它试图成为一个 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 方法签名以返回 Option&lt;V&gt; 而不是 Option&lt;&amp;V&gt; 但这也导致了问题 2,我似乎无法克隆共享的值参考。

问题 2
move occurs because value has type `K`, which does not implement the `Copy` trait
help: consider borrowing here: `&*k.clone()`"

AFAIK 我不想将 Copy 特征设置为 V 的绑定,因为它可能不是一个简单的类型,并且宁愿克隆它的值而不是将其设置为 API 限制。我似乎无法解决这个问题。

【问题讨论】:

  • RefCell 应该可以帮助您使get 接受&amp;self,同时仍然能够更改backend
  • 如果你先self.backend.set,然后return self.backend.get(cache_k);,问题2可以很容易解决,除非性能太关键

标签: rust


【解决方案1】:
  1. 问题1:可变引用和不可变引用可能共存。这里最好的选择是什么?我尝试更改get 方法签名以返回Option&lt;V&gt; 而不是Option&lt;&amp;V&gt; 但这也导致我遇到问题2,我似乎无法克隆共享引用的值。

    您可以将get_or_insert 方法添加到特征,并使用hash_map::Entry API 为MemoryBackend 实现它:

    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&lt;V&gt; 的函数:

    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. 问题 2“移动发生是因为值的类型为K,它没有实现Copy trait 帮助:考虑在这里借用:&amp;*k.clone()" AFAIK 我不想将 Copy 特征设置为 V 的绑定,因为它可能不是一个简单的类型,并且宁愿克隆它的值而不是将其设置为 API 限制。 所以我似乎无法解决这个问题。

    您可以在实现中添加K: CloneV: Clone 约束:

    impl<'a, K, V> Cache<'a, K, V>
    where
        K: Clone + std::cmp::Eq + std::hash::Hash + 'static,
        V: Clone + 'static,
    {
        // etc
    }
    

(Playground)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-07-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-12-08
    • 2014-07-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多