将泛型类型绑定到需要生命周期的特征时未使用的类型参数

时间:2017-04-28 16:19:56

标签: rust

use std::collections::HashMap;
use std::hash::Hash;

struct Watchable<'a, K, V, W: Watcher<'a, K, V>> {
    data: HashMap<K, V>,
    watchers: Vec<W>,
}

trait Watcher<'a, K, V> {
    fn before_new(&mut self, key: &'a K, value: &'a V);
}

struct IndexWatcher<'a, I: 'a, V: 'a> {
    data: HashMap<&'a I, &'a V>,
    indexer: fn(&V) -> &I,
}

impl<'a, K, V, I> Watcher<'a, K, V> for IndexWatcher<'a, I, V>
    where I: Eq + Hash
{
    fn before_new(&mut self, key: &'a K, value: &'a V) {
        let index = (self.indexer)(value);
        self.data.insert(index, value);
    }
}
error[E0392]: parameter `'a` is never used
 --> src/main.rs:4:18
  |
4 | struct Watchable<'a, K, V, W: Watcher<'a, K, V>> {
  |                  ^^ unused type parameter
  |
  = help: consider removing `'a` or using a marker such as `std::marker::PhantomData`

有没有办法删除一些生命周期注释?似乎一切都有相同的生命期a

起初,我没有任何具体的生命周期:

struct IndexWatcher<I, V> {
    data: HashMap<&I, &V>,
    indexer: fn(&V) -> &I,
}

编译器抱怨生命周期,所以我添加了它:

struct IndexWatcher<'a, I: 'a, V: 'a> {
    data: HashMap<&'a I, &'a V>,
    indexer: fn(&V) -> &I,
}

当我试图在没有生命期的情况下实现这个特性时:

trait Watcher<K, V> {
    fn before_new(&mut self, key: &K, value: &V);
}

impl<'a, K, V, I> Watcher<K, V> for IndexWatcher<'a, I, V>
    where I: Eq + Hash
{
    fn before_new(&mut self, key: &K, value: &V) {
        let index = (self.indexer)(value);
        self.data.insert(index, value);
    }
}

我收到了错误:

error[E0312]: lifetime of reference outlives lifetime of borrowed content...
  --> <anon>:18:33
   |
18 |         self.data.insert(index, value);
   |                                 ^^^^^
   |

error[E0495]: cannot infer an appropriate lifetime for lifetime parameter in function call due to conflicting requirements
  --> <anon>:17:21
   |
17 |         let index = (self.indexer)(value);
   |                     ^^^^^^^^^^^^^^^^^^^^^
   |

因此,我的最终版本到处都有生命。

理想情况下,我想使用Watchable中的特征,如下所示:

impl<K, V, W: Watcher<K, V>> Watchable<K, V, W>
    where K: Eq + Hash {

    fn insert(&mut self, k: K, v: V) -> Option<V> {
        match self.data.entry(k) {
            Occupied(mut occupied) => {
                {
                    let k = occupied.key();
                    let old = occupied.get();
                    for watcher in &mut self.watchers {
                        watcher.before_change(k, &v, old);
                    }
                }
                let old = occupied.insert(v);
                Some(old)
            },
            Vacant(vacant) => {
                {
                    let k = vacant.key();
                    for watcher in &mut self.watchers {
                        watcher.before_new(k, &v);
                    }
                }
                vacant.insert(v);
                None
            }
        }
    }
}

trait Watcher<K, V> {
    fn before_new(&mut self, key: &K, value: &V);
    fn before_change(&mut self, key: &K, value: &V, old: &V);
}

1 个答案:

答案 0 :(得分:2)

您可以使用higher-rank trait bound代替生命周期参数来消除错误:

struct Watchable<K, V, W: for<'a> Watcher<'a, K, V>> {
    data: HashMap<K, V>,
    watchers: Vec<W>,
}

但这并不能解决你的问题。 IndexWatcher不会满足绑定for<'a> Watcher<'a, K, V>,因为IndexWatcher<'a, I, V>仅针对一个特定生命周期实现Watcher<'a, K, V>,而不是针对所有可能的生命周期。

从根本上说,问题在于you're trying to put a value and a reference to that value in the same struct(间接)。也就是说,您的想法是观察者应该从Watchable借用数据,但Watchable也拥有观察者。请花点时间到read this question和Shepmaster的答案,了解为什么这个想法不起作用。

特别要注意的是,在Watchable的{​​{1}}中插入条目或删除条目可能会导致任何观察者的引用无效,因为HashMap需要重新分配存储,可能导致密钥和值的地址发生变化。

我要做的是将密钥和值包装在HashMap(或Rc中,如果您想跨线程共享Arc。但是,从Watchable中获取共享索引值可能会有问题。考虑将索引函数更改为Rc<V>,并使索引函数返回克隆(如果克隆索引值太贵,它们可以是fn(&V) -> I的克隆)或处理。