如何遍历实现Index和IntoIterator的通用集合的索引?

时间:2019-10-06 19:41:40

标签: generics collections rust traits

我想为实现Cstd::ops::Index的任何通用集合std::iter::IntoIterator实现一个小的有向图特征。我希望集合代表图的节点。每个节点都由其在C中的索引表示,该索引可以是usize的{​​{1}}索引,或者Vec的{​​{1}}键。 我不知道这是否是图库的最佳方法,但我也想了解Rust,泛型特征和Rust的标准库。

在实现的某些时候,我需要遍历String实例的所有索引。我发现执行此操作的唯一方法是HashMap函数,但这仅对迭代器实现C计数器,而不对我的泛型类型实现,因此它对enumerate有效不适用于usize

这是使用Vec实现的样子。需要HashMapenumerate函数,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函数。

我该如何以一种简洁通用的方式编写代码?

1 个答案:

答案 0 :(得分:0)

我找到了一种方法:

说明

我意识到我希望Index提供一种enumerate方法来枚举索引以及集合索引后面的项目。因此,借助公认的答案Using generic iterators instead of specific list types,我实现了Index的超特性,它提供了这种方法。

步骤1:定义一个新的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())
        })
    }
}

第2步:为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>;
}

第3步:为我要使用的集合实现上级特征

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
  • 在每个实现中,我们必须确保两个迭代器的顺序一致。

如果我的实现中有任何问题,请告诉我!看来工作正常,但我只了解自己在做的一半。