[英]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.