gpt4 book ai didi

rust - 使用具有引用值的借用特征

转载 作者:行者123 更新时间:2023-12-05 05:53:06 27 4
gpt4 key购买 nike

我有一个要在 HashMap<(u32, u32), &'a Edge> 上实现的自定义特征.因为我希望我的特征对拥有的和引用的值都有效,所以我使用 Borrow其他 SO 帖子建议的特征,但我对借用检查器和引用的 &Edge 有疑问.首先,编译器要我专门为 &Edge 添加生命周期。正如您在下面看到的那样,我做了这些。

但是,编译器会提示函数的返回类型 get_edge ( Option<&'a Edge> ) 与特征定义 ( Option<&Edge> ) 的返回类型不匹配。在我看来,将生命周期参数添加到我的特征定义中没有意义,所以我猜错误一定是在我实现特征的某个地方。然而,无论我尝试什么样的生命周期参数组合,我都无法让编译器满意。我到底做错了什么?

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:为了添加一点上下文,我尝试围绕 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 {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^

最佳答案

完整的解决方案: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);
}

首先,EdgeFinder特性在 HashMap<(u32, u32), &Edge> 上实现.

二、Borrow<HasMap<K,V>>特性在 HasMap<K,V> 上实现和 &HasMap<K,V> ,因为 Borrow特质提供 Borrow<X> X 的一揽子实现和 &X (docs) .

所以,T: Borrow<U>, U: EdgeFinder HashMap<(u32, u32), &Edge> 满足特征界限和 &HashMap<(u32, u32), &Edge>作为T .

这使得 EdgeViewer::new接受 HashMap<(u32, u32), &Edge>&HashMap<(u32, u32), &Edge> , 并给出 EdgeViewer访问 EdgeFinder通过它的特征 inner字段。

Deref trait 是为了方便而实现的,但是 getter 到 inner也可以使用。

PhantomData需要消除 U 上的歧义类型。事实上,Borrow<U>是 U 的通用特征,因此 Borrow可以在几种类型上实现。感谢PhantomData , 我们可以指定 U 为 HashMap<(u32, u32), &Edge> .至少,我是这样理解的。

注1:EdgeFinderHashMap<(u32, u32), &'a Edge> 上实现和 &HashMap<(u32, u32), &'a Edge>不是有效答案,因为它需要多个 EdgeFinder实现。 (see playground)

注2:AsRef trait 在这里不容易使用,因为它没有在 HasMap<K,V> 上实现和 &HasMap<K,V>

关于rust - 使用具有引用值的借用特征,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69975427/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com