【问题标题】:How do I express generic map and set containers in Rust?如何在 Rust 中表达通用地图和设置容器?
【发布时间】:2018-12-03 14:10:12
【问题描述】:

我正在学习 Rust 来自 C++ 背景,我正在编写拓扑排序。

输入是一个类型为Map<Key, Set<Key>> 的依赖映射,其中每个节点(键)都映射到它的依赖(一组键)。 MapSet 可以是任何 MapSet 实现。输出是一个具有排序拓扑顺序的向量。

在 C++ 中,我会为 MapSet 使用“模板模板参数”:

template<
    class K,
    template<class...> class Map,
    template<class...> class Set
>
std::vector<K>
topological_sort(Map<K, Set<K>> const &depmap);

此功能可应用于map&lt;Key, set&lt;Key&gt;&gt;unordered_map&lt;Key, set&lt;Key&gt;&gt;map&lt;Key, unordered_set&lt;Key&gt;&gt;

在 Rust 中,似乎没有“模板模板参数”。我可以写如下:

fn topological_sort<K: Eq + Ord + Hash + Clone>(depmp: &BTreeMap<K, HashSet<K>>) -> Option<Vec<K>> {
}

但是代码在容器选择方面不是通用的,因为它不适用于 HashMap&lt;K, HashSet&lt;K&gt;&gt; 等。

我尝试了假设的语法:

fn topological_sort<Map, Set, K: Eq + Ord + Hash + Clone>(depmp: &Map::<K, Set::<K>>) -> Option<Vec<K>>

这不起作用。 Rust 对泛型容器的解决方案是什么?

【问题讨论】:

  • 这个特性也被称为 higher kinded types,Rust 还没有。此外,与 C++ 相比,在 Rust 中,您只能访问由 trait bound 提供的泛型类型参数上的方法和关联类型。 Rust 标准库中没有描述通用映射的特征,因此您必须定义自己的特征并为键类型使用关联类型。
  • 看看 Rust 中的迭代器。
  • 您可能可以使用 M: Index&lt;K, Output=HashSet&lt;K&gt;&gt; 之类的东西抽象地图类型,但我认为没有抽象集合类型的等价物。
  • 虽然你可以在HashSetBTreeSet之间写出你自己的特征来抽象……
  • @SvenMarnach 所以我需要写trait GenericSet&lt;K&gt;trait GenericMap&lt;K, V&gt;,然后是impl&lt;K&gt; GenericSet&lt;K&gt; for HashSet&lt;K&gt;,等等。这很有道理,谢谢。

标签: rust generic-programming


【解决方案1】:

Rust 对于通用容器的解决方案是什么?

通用容器的理想解决方案不可用。这将由目前处于实施阶段的功能generic associated types (GATs) 覆盖。

目前,有一些方法可以使您的例程适用于某些用例。特别是,函数通过实现IntoIterator的值接收任意数据序列是很常见的:

fn my_number_process<I>(stream: I) -> f32
where
    I: IntoIterator<Item = f32>,
{
    stream.into_iter().map(|x| x * 2. + 5.).sum().unwrap_or(0.)
}

对于类似字典的容器,IndexIndexMut 特征公开了通过具有已知类型的键获取对接收器中的值的引用的特定功能。两种情况下的方法都返回&amp;Self::Output,没有为可恢复的错误或其他类型的输出留下空间。 作为替代方案,您可以创建一个适合该目的的新特征,同时尝试克服缺乏高级类型的问题。特别是,对于普通的HashMap,无法实现以下特征:

trait IMap<K> {
    type Value;

    fn get<B: Borrow<K>>(&self, key: B) -> Option<Self::Value>;
}

这是因为我们不能将Value 指定为&amp;'a V,其中'a 是一个生命周期,它将被实例化为self 的生命周期。不过可以实现引用HashMap

impl<'a, K, V> IMap<K> for &'a HashMap<K, V>
where
    K: Eq,
    K: Hash,
{
    type Value = &'a V;

    fn get<B: Borrow<K>>(&self, key: B) -> Option<Self::Value> {
        HashMap::get(self, key.borrow())
    }
}

Playground

类似的推理可以用于通用 Set 容器。

【讨论】:

    【解决方案2】:

    这是我能来的最接近的:

    use std::collections::*;
    use std::hash::Hash;
    use std::ops::Index;
    
    trait Set<K> {
        fn does_contain(&self, value: &K) -> bool;
    }
    impl<K: Eq + Hash> Set<K> for HashSet<K> {
        fn does_contain(&self, value: &K) -> bool {
            self.contains (value)
        }
    }
    impl<K: Eq + Ord> Set<K> for BTreeSet<K> {
        fn does_contain(&self, value: &K) -> bool {
            self.contains (value)
        }
    }
    
    fn topological_sort<K, S: Set<K>, M: Index<K, Output=S>> (depmp: &M) -> Option<Vec<K>> {
        unimplemented!()
    }
    

    它使用std::ops::Index 对映射类型进行抽象,并使用自定义Set 特征对集合类型进行抽象。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-12-16
      • 2023-01-07
      • 2014-10-21
      • 1970-01-01
      • 2020-05-24
      • 1970-01-01
      • 2017-06-09
      • 1970-01-01
      相关资源
      最近更新 更多