rust - 使用带有引用值的 Borrow trait
问题描述
我有一个要在HashMap<(u32, u32), &'a Edge>
. 由于我希望我的特征同时适用于拥有和引用的值,因此我使用Borrow
其他 SO 帖子建议的特征,但我在借用检查器和引用的&Edge
. 首先,编译器希望我专门添加&Edge
我所做的生命周期,如下所示。
但是,编译器会抱怨函数get_edge
( Option<&'a Edge>
) 的返回类型与特征定义 ( ) 的返回类型不匹配Option<&Edge>
。在我看来,在我的 trait 定义中添加生命周期参数是没有意义的,所以我猜这个错误一定是在我的 trait 实现中的某个地方。然而,无论我尝试什么样的生命周期参数组合,我都无法让编译器满意。我在这里到底做错了什么?
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()))
}
}
编辑:
我添加了Edge
上面的定义,虽然它不重要。这是编译器输出:
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>`
编辑 2:
为了添加一点上下文,我尝试围绕 an 创建一个包装器结构,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 }
}
}
但是,当使用引用时,会显示以下编译器输出,这让我相信我需要专门为引用的版本添加一个实现。
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 {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
解决方案
完整的解决方案:游乐场
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);
}
首先,EdgeFinder
trait 在HashMap<(u32, u32), &Edge>
.
其次,Borrow<HasMap<K,V>>
特征是在 and 上实现的HasMap<K,V>
,&HasMap<K,V>
因为Borrow
trait为and (docs)Borrow<X>
提供了全面的实现。X
&X
因此,T: Borrow<U>, U: EdgeFinder
trait bound 由HashMap<(u32, u32), &Edge>
和&HashMap<(u32, u32), &Edge>
as满足T
。
这使得EdgeViewer::new
接受HashMap<(u32, u32), &Edge>
和&HashMap<(u32, u32), &Edge>
,并通过其字段提供EdgeViewer
对特征的访问。EdgeFinder
inner
Deref
trait 是为了方便而实现的,但inner
也可以使用 getter to。
PhantomData
需要消除U
类型的歧义。确实,Borrow<U>
是 U 的通用特征,因此Borrow
可以在多种类型上实现。多亏了PhantomData
,我们可以指定 U 是HashMap<(u32, u32), &Edge>
。至少,我是这样理解的。
注意 1:EdgeFinder
实现 onHashMap<(u32, u32), &'a Edge>
并且&HashMap<(u32, u32), &'a Edge>
不是有效答案,因为它需要多个EdgeFinder
实现。
(见操场)
注意 2:AsRef
这里不能轻易使用 trait,因为它没有在HasMap<K,V>
and上实现&HasMap<K,V>
推荐阅读
- hyperledger-fabric - 技术问题 - Hyperledger Fabric - 错误:获取背书客户端进行调用时出错:背书客户端 > 无法连接到
- amazon-web-services - Route53 安全证书
- c++ - 简单 C++ 程序:'C:\Program' 不是内部或外部命令、可运行程序或批处理文件
- java - 缓冲与无缓冲。缓冲区实际上是如何工作的?
- javascript - 从嵌套对象中的键或其值获取父对象
- apache-flink - 替代已弃用的方法 DataStream.keyBy()
- javascript - 如何使用 async-await 等待表创建/使其在 Jest 单元测试中持续存在?
- python - 单独读取和处理 CSV 文件,将结果输出到新的单个文件
- android - 如何在 Android 的 Twilio 中删除参与者侦听器
- excel - 是否可以在 excel 中创建一个下拉列表,显示访问表中特定字段的数据?