简体   繁体   English

使用带有引用值的 Borrow trait

[英]Using Borrow trait with a referenced value

I have a custom trait that I want to implement on a HashMap<(u32, u32), &'a Edge> .我有一个自定义特征,我想在HashMap<(u32, u32), &'a Edge> Since I want my trait to work on both owned and referenced values, I use the Borrow trait as suggested by other SO posts but I am having issues with the borrow checker and the referenced &Edge .由于我希望我的特征同时适用于拥有的和引用的值,因此我按照其他 SO 帖子的建议使用了Borrow特征,但是我在借用检查器和引用的&Edge遇到了问题。 First, the compiler wants me to specifically add a lifetime to &Edge which I did as you can see below.首先,编译器希望我专门为&Edge添加一个生命周期,正如您在下面看到的那样。

However, then the compiler complains that the return type of the function get_edge ( Option<&'a Edge> ) does not match the return type of the trait definition ( Option<&Edge> ).但是,编译器会抱怨函数get_edge ( Option<&'a Edge> ) 的返回类型与特征定义 ( Option<&Edge> ) 的返回类型不匹配。 In my opinion, it does not make sense to add lifetime parameters to my trait definition so I guess the error must be somewhere along my implementation of the trait.在我看来,将生命周期参数添加到我的特征定义中是没有意义的,所以我猜错误一定是在我实现特征的某个地方。 However, no matter what combinations of lifetime parameters I try, I haven't been able to make the compiler happy.然而,无论我尝试什么样的生命周期参数组合,我都无法让编译器满意。 What exactly am I doing wrong here?我到底做错了什么?

pub struct Edge {
    between: (u32, u32),
    weight: u32,
}

impl Edge {
    pub fn normalize_edge(v1: u32, v2: u32) -> (u32, u32) {
        (v1.min(v2), v1.max(v2))
    }

    fn get_weight(&self) -> u32 {
        self.weight
    }
}

trait EdgeFinder {
    fn get_edge(&self, v1: u32, v2: u32) -> Option<&Edge>;
    fn get_weight(&self, v1: u32, v2: u32) -> Option<u32>;
}

impl<'a, 'b, B: Borrow<HashMap<(u32, u32), &'a Edge>> + 'b> EdgeFinder for B {
    fn get_edge(&self, v1: u32, v2: u32) -> Option<&Edge> {
        self.borrow().get(&Edge::normalize_edge(v1, v2)).map(|&e| e)
    }

    fn get_weight(&self, v1: u32, v2: u32) -> Option<u32> {
        self.get_edge(v1, v2).and_then(|v| Some(v.get_weight()))
    }
}

Edit: I added the definition of Edge above although it should not matter.编辑:我在上面添加了Edge的定义,尽管这无关紧要。 Here is the compiler output:这是编译器输出:

error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
  --> src/graph.rs:44:23
   |
44 |         self.borrow().get(&(0,0)).map(|&e| e)
   |                       ^^^
   |
note: first, the lifetime cannot outlive the lifetime `'a` as defined on the impl at 41:6...
  --> src/graph.rs:41:6
   |
41 | impl<'a, 'b, B: 'b + Borrow<HashMap<(u32, u32), &'a Edge>>> EdgeFinder for B {
   |      ^^
note: ...so that the types are compatible
  --> src/graph.rs:44:23
   |
44 |         self.borrow().get(&(0,0)).map(|&e| e)
   |                       ^^^
   = note: expected `&HashMap<(u32, u32), &Edge>`
              found `&HashMap<(u32, u32), &'a Edge>`
note: but, the lifetime must be valid for the anonymous lifetime defined on the method body at 42:17...
  --> src/graph.rs:42:17
   |
42 |     fn get_edge(&self, v1: u32, v2: u32) -> Option<&Edge> {
   |                 ^^^^^
note: ...so that the expression is assignable
  --> src/graph.rs:44:9
   |
44 |         self.borrow().get(&(0,0)).map(|&e| e)
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   = note: expected `Option<&Edge>`
              found `Option<&Edge>`

Edit 2: To add a little bit of context, I try to create a wrapper structure around an EdgeFinder but I don't want to enforce whether the wrapped EdgeFinder is owned or a reference.编辑 2:为了添加一点上下文,我尝试围绕EdgeFinder创建一个包装器结构,但我不想强制要求包装的EdgeFinder是拥有的还是引用的。

pub struct EdgeViewer<T: EdgeFinder> {
    inner: T,
}

impl<T: EdgeFinder> EdgeViewer<T> {
  //Obviously works for owned values.
  pub fn new(inner: T) -> Self {
     EdgeViewer { inner }
  }
}

However, when using a reference, the following compiler output is shown, which led me to believe that I need to specifically add an implementation for the referenced version.但是,在使用引用时,会显示以下编译器输出,这让我相信我需要为引用版本专门添加一个实现。

error[E0277]: the trait bound `&HashMap<(u32, u32), &Edge>: EdgeFinder` is not satisfied
  --> src/construction.rs:74:43
   |
74 |     let mut edge_viewer = EdgeViewer::new(&edges);
   |                                           -^^^^^
   |                                           |
   |                                           the trait `EdgeFinder` is not implemented for `&HashMap<(u32, u32), &Edge>`
   |                                           help: consider removing the leading `&`-reference
   |
   = help: the following implementations were found:
             <HashMap<(u32, u32), &Edge> as EdgeFinder>
note: required by `EdgeViewer::<T>::new`
  --> src/graph.rs:58:5
   |
58 |     pub fn new(inner: T) -> Self {
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^

The complete solution: playground完整的解决方案: 游乐场

use std::{borrow::Borrow, collections::HashMap, marker::PhantomData, ops::Deref};

#[derive(Debug)]
pub struct Edge {
    between: (u32, u32),
    weight: u32,
}

impl Edge {
    pub fn normalize_edge(v1: u32, v2: u32) -> (u32, u32) {
        (v1.min(v2), v1.max(v2))
    }

    fn get_weight(&self) -> u32 {
        self.weight
    }
}

pub trait EdgeFinder {
    fn get_edge(&self, v1: u32, v2: u32) -> Option<&Edge>;
    fn get_weight(&self, v1: u32, v2: u32) -> Option<u32>;
}

impl EdgeFinder for HashMap<(u32, u32), &Edge> {
    fn get_edge(&self, v1: u32, v2: u32) -> Option<&Edge> {
        self.get(&Edge::normalize_edge(v1, v2)).copied()
    }

    fn get_weight(&self, v1: u32, v2: u32) -> Option<u32> {
        self.get_edge(v1, v2).map(|v| v.get_weight())
    }
}

pub struct EdgeViewer<T, U>
where
    T: Borrow<U>,
    U: EdgeFinder,
{
    inner: T,
    __phantom: PhantomData<U>,
}

impl<T, U> EdgeViewer<T, U>
where
    T: Borrow<U>,
    U: EdgeFinder,
{
    pub fn new(inner: T) -> Self {
        EdgeViewer {
            inner,
            __phantom: PhantomData,
        }
    }
}

impl<T, U> Deref for EdgeViewer<T, U>
where
    T: Borrow<U>,
    U: EdgeFinder,
{
    type Target = T;
    fn deref(&self) -> &Self::Target {
        &self.inner
    }
}

fn main() {
    let e1 = Edge {
        between: (0, 1),
        weight: 1,
    };

    let e2 = Edge {
        between: (1, 2),
        weight: 1,
    };

    let mut map = HashMap::new();
    map.insert((0, 1), &e1);
    map.insert((1, 2), &e2);

    let viewer: EdgeViewer<_, HashMap<(u32, u32), &Edge>> = EdgeViewer::new(&map);

    let found_edge = viewer.get_edge(0, 1);

    println!("{:?}", found_edge);
}

First, EdgeFinder trait is implemented on HashMap<(u32, u32), &Edge> .首先, EdgeFinder trait 在HashMap<(u32, u32), &Edge> EdgeFinder , EdgeFinder HashMap<(u32, u32), &Edge>

Second, the Borrow<HasMap<K,V>> trait is implemented on HasMap<K,V> and &HasMap<K,V> , because Borrow trait provides Borrow<X> blanket implementations for X and &X (docs) .其次, Borrow<HasMap<K,V>> trait 在HasMap<K,V>&HasMap<K,V> ,因为Borrow trait 为X&X (docs)提供了Borrow<X>全面实现。

So, T: Borrow<U>, U: EdgeFinder trait bound is satisfied by HashMap<(u32, u32), &Edge> and &HashMap<(u32, u32), &Edge> as T .所以, T: Borrow<U>, U: EdgeFinder trait bound 满足HashMap<(u32, u32), &Edge>&HashMap<(u32, u32), &Edge>作为T

This makes EdgeViewer::new accepts HashMap<(u32, u32), &Edge> and &HashMap<(u32, u32), &Edge> , and gives EdgeViewer access to EdgeFinder trait through its inner field.这使得EdgeViewer::new接受HashMap<(u32, u32), &Edge> EdgeViewer::new HashMap<(u32, u32), &Edge>&HashMap<(u32, u32), &Edge> EdgeViewer , EdgeFinder &HashMap<(u32, u32), &Edge> ,并让EdgeViewer通过其inner字段访问EdgeFinder trait。

Deref trait is implemented for convenience, but getter to inner could also be used. Deref trait 是为了方便而实现的,但也可以使用inner getter。

PhantomData is required to remove ambiguity on U type.需要PhantomData来消除U类型的歧义。 indeed, Borrow<U> is a generic trait over U, and so Borrow could be implemented on several types.实际上, Borrow<U>Borrow<U>上的通用特征,因此可以在多种类型上实现Borrow Thanks to PhantomData , we can specify that U is HashMap<(u32, u32), &Edge> .感谢PhantomData ,我们可以指定 U 是HashMap<(u32, u32), &Edge> PhantomData , PhantomData HashMap<(u32, u32), &Edge> At least, this is how i understant it.至少,这是我理解它的方式。

Note1: EdgeFinder implementation on HashMap<(u32, u32), &'a Edge> and &HashMap<(u32, u32), &'a Edge> is not a valid answer cause it requires several EdgeFinder implementations.注 1: HashMap<(u32, u32), &'a Edge> EdgeFinder HashMap<(u32, u32), &'a Edge>&HashMap<(u32, u32), &'a Edge>上的EdgeFinder实现不是有效答案,因为它需要多个EdgeFinder实现。 (see playground) (见游乐场)

Note 2: AsRef trait can not be used easily here, because it is not implemented on HasMap<K,V> and &HasMap<K,V>注 2: AsRef trait 在这里不能轻易使用,因为它没有在HasMap<K,V>&HasMap<K,V>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM