我想为实现C
和std::ops::Index
的任何通用集合std::iter::IntoIterator
实现一个小的有向图特征。我希望集合代表图的节点。每个节点都由其在C
中的索引表示,该索引可以是usize
的{{1}}索引,或者Vec
的{{1}}键。
我不知道这是否是图库的最佳方法,但我也想了解Rust,泛型特征和Rust的标准库。
在实现的某些时候,我需要遍历String
实例的所有索引。我发现执行此操作的唯一方法是HashMap
函数,但这仅对迭代器实现C
计数器,而不对我的泛型类型实现,因此它对enumerate
有效不适用于usize
。
这是使用Vec
实现的样子。需要HashMap
和enumerate
函数,nodes
返回图的邻接信息。要使用children
函数获得所有先前版本,我需要遍历通用容器类型的索引。
children
这给了我不太令人惊讶的编译器错误:
parents
一个 ugly 解决方案是添加另一个必需的方法pub trait SimpleGraph {
/// type used as an index to refer to nodes.
type I: Eq + std::hash::Hash;
/// container type for the nodes
type C: std::ops::Index<Self::I> + std::iter::IntoIterator<Item = Self::I>;
/// returns a reference to the node container.
fn nodes(&self) -> &Self::C;
/// gets the indices of the children of a node with index `index`.
fn children(&self, index: Self::I) -> Vec<Self::I>;
/// gets all ancestors of a node (not very efficient...)
fn parents(&self, i: Self::I) -> Vec<Self::I> {
let mut res = Vec::<Self::I>::new();
let nodes = self.nodes();
for (idx, _) in nodes.into_iter().enumerate() {
let children = self.children(idx);
for child_idx in children {
if child_idx == i {
res.push(idx);
}
}
}
return res;
}
}
,该方法返回索引列表,然后在该列表上进行迭代,但这不是非常用户友好,并且看起来像一个对于同时实现error[E0308]: mismatched types
--> src/lib.rs:19:42
|
19 | let children = self.children(idx);
| ^^^ expected associated type, found usize
|
= note: expected type `<Self as SimpleGraph>::I`
found type `usize`
和indices
的{{1}}中的任何一个而言,都是不必要的步骤。我宁愿以一种通用的方式覆盖std::collections
函数。
我该如何以一种简洁通用的方式编写代码?
答案 0 :(得分:0)
我找到了一种方法:
我意识到我希望Index
提供一种enumerate
方法来枚举索引以及集合索引后面的项目。因此,借助公认的答案Using generic iterators instead of specific list types,我实现了Index
的超特性,它提供了这种方法。
Enumerate
结构并为其实现Iterator
pub struct Enumerate<IndexIter, ItemIter> {
index: IndexIter,
item: ItemIter,
}
/// implements the [`Iterator`] trait for the new struct
impl<IndexIter, ItemIter> Iterator for Enumerate<IndexIter, ItemIter>
where
IndexIter: Iterator,
ItemIter: Iterator,
{
type Item = (IndexIter::Item, ItemIter::Item);
/// returns the next iterator
#[inline]
fn next(&mut self) -> Option<(IndexIter::Item, ItemIter::Item)> {
self.index.next().map(|idx| {
// CAUTION! We need to make sure that the index and item iterators are ordered consistently.
// We are really just incrementing two iterators simultaneously here...
(idx, self.item.next().unwrap())
})
}
}
Index
定义超级特征,并添加enumerate
方法/// trait for implementing over the indices of collections that implement [`std::ops::Index`].
///
/// It adds the enumerate function that returns an `Enumerate<IndexIter,ItemIter>` as an iterator.
pub trait SuperIndex<'a, Idx>: std::ops::Index<Idx> {
type IndexIter: Iterator<Item = Idx>;
type ItemIter: Iterator;
/// enumerates over the indices and items of a collection
fn enumerate(&'a self) -> Enumerate<Self::IndexIter, Self::ItemIter>;
}
Vec
的实现/// implement the [`SuperIndex`] trait for [`Vec<T>`]
impl<'a, T: 'a> SuperIndex<'a, usize> for Vec<T> {
type IndexIter = std::ops::Range<usize>;
type ItemIter = std::slice::Iter<'a, T>;
fn enumerate(&'a self) -> Enumerate<Self::IndexIter, Self::ItemIter> {
Enumerate {
index: 0..self.len(),
item: self.iter(),
}
}
}
HashMap
的实现/// implement the [`SuperIndex`] trait for [`HashMap<K, V, S>`]
impl<'a, K: 'a, V: 'a, S> SuperIndex<'a, &'a K> for std::collections::HashMap<K, V, S>
where
K: Eq + std::hash::Hash,
S: std::hash::BuildHasher,
{
type IndexIter = std::collections::hash_map::Keys<'a, K, V>;
type ItemIter = std::collections::hash_map::Values<'a, K, V>;
fn enumerate(&'a self) -> Enumerate<Self::IndexIter, Self::ItemIter> {
Enumerate {
index: self.keys(),
item: self.values(),
}
}
}
现在,我可以枚举实现SuperIndex
的任何种类的集合的索引和值,而index
不必是usize
:
for (index, item) in c.enumerate() {
assert_eq!(&c[index], item);
}
此实现实现了我想要的,我想不出任何替代方法,但是有一些小缺陷:
SuperIndex
索引的通用性不能与Index
的索引通用,例如不允许切片。SuperIndex
。如果我的实现中有任何问题,请告诉我!看来工作正常,但我只了解自己在做的一半。