返回一个在稳定的Rust中对HashMap条目进行参数化的迭代器

huangapple go评论125阅读模式
英文:

Returning a parametrized iterator over HashMap entries in stable Rust

问题

我试图扩展一个 Rust 容器(HashMap),添加一个类似于 iter() 的方法,该方法返回一个非拥有的迭代器。这个方法将遍历与 HashMap::iter() 相同的条目,但会过滤掉一些条目。我希望使用一个 &str 参数来参数化过滤(因此取决于参数,可能会跳过映射的一些、没有或所有的 (&k, &v) 条目)。

我决定使用扩展 trait 进行实现,并在新方法中使用 iter().filter_map()。

use std::collections::{hash_map, HashMap};

pub struct Section {}

pub trait HashMapExt<'a> {
    type FilterMap: Iterator<Item = &'a Section>;

    fn iter_sections(&'a self, path: &'static str) -> Self::FilterMap;
}

一个未捕获参数的虚构实现可以正常工作:

impl<'a> HashMapExt<'a> for HashMap<String, Section> {
    type FilterMap = core::iter::FilterMap<hash_map::Iter<'a, String, Section>,
                                           fn((&String, &'a Section))->Option<&'a Section>>;

    fn iter_sections(&'a self, path: &str) -> Self::FilterMap {
        self.iter().filter_map(|(k,v)| {
            println!("do stuff"); // 模拟过滤
            Some(v)
        })
    }
}

但是当我尝试捕获 "path" 时出现问题:

fn iter_sections(&'a self, path: &str) -> Self::FilterMap {
    self.iter().filter_map(|(k,v)| {
        path;  // 模拟基于 "path" 的过滤
        Some(v)
    }) 
}

这导致了在 filter_map() 行出现错误:

error[E0308]: mismatched types
expected fn pointer, found closure
note: expected fn pointer `for<'r> fn((&'r String, &Section)) -> Option<&Section>`
      found closure `[closure@src/main.rs:31:32: 31:57]`

我没有问题返回由闭包类型参数化的 FilterMap 类型,但问题是 Rust 中这个 [closure@src/main.rs...] 的东西没有类型名称。

这就是我的问题:我如何指定一个接受参数化过滤器的 trait 方法的返回类型,以便获得一个正常工作的 iter_sections() 方法?

我迄今为止尝试解决这个问题的努力:

  1. 我想也许使用非参数化的函数指针可以解决问题:
fn make_filter<'a>(path: &'static str) -> impl Fn((&String, &'a Section))->Option<&'a Section> {
    move|(p, c)| {
        path;  // 模拟基于 "path" 的过滤
        Some(c)
    }
}

在使用时:

fn iter_sections(&'a self, path: &'static str) -> Self::FilterMap {
    let filter = make_filter(path);
    self.iter().filter_map(filter)
}

这并没有帮助:

error[E0308]: mismatched types
expected fn pointer, found opaque type
note: expected fn pointer `for<'r> fn((&'r String, &Section)) -> Option<&Section>`
      found opaque type `impl for<'r> Fn<((&'r String, &Section),)>`
  1. 我尝试在 trait 实现中返回一个 "impl" 类型:
type FilterMap = core::iter::FilterMap<hash_map::Iter<'a, String, Section>,
                                       impl Fn((&String, &'a Section))->Option<&'a Section>>;

显然在稳定版的 Rust 中不可行:

error[E0658]: `impl Trait` in type aliases is unstable

note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information

我使用的是 Rust v1.48,所以这对我来说不是一个选择。我知道关于 "Box" 的解决方案(使 trait 处理 Box 返回值),但我想尽量避免堆分配,并通过类型操作来表示迭代器类型。

英文:

I'm trying to extend a Rust container (HashMap) with a method that returns a non-owning iterator, similarly to iter(). This method would iterate over the same entries as HashMap::iter() but will filter out some entries. I want to parametrize the filtering with a &str parameter (so depending on the parameter, some, none, or all the map's (&k, &v) entries are stepped over).

I decided to use an extension trait for the implementation, and in the new method, just summon iter().filter_map().

use std::collections::{hash_map, HashMap};

pub struct Section {}

pub trait HashMapExt&lt;&#39;a&gt; {
    type FilterMap: Iterator&lt;Item = &amp;&#39;a Section&gt;;

    fn iter_sections(&amp;&#39;a self, path: &amp;&#39;static str) -&gt; Self::FilterMap;
}

A dummy implementation that is not capturing the parameter in a closure works fine:

impl&lt;&#39;a&gt; HashMapExt&lt;&#39;a&gt; for HashMap&lt;String, Section&gt; {
    type FilterMap = core::iter::FilterMap&lt;hash_map::Iter&lt;&#39;a, String, Section&gt;,
                                           fn((&amp;String, &amp;&#39;a Section))-&gt;Option&lt;&amp;&#39;a Section&gt;&gt;;

    fn iter_sections(&amp;&#39;a self, path: &amp;str) -&gt; Self::FilterMap {
        self.iter().filter_map(|(k,v)| {
            println!(&quot;do stuff&quot;); // Simulate filtering
            Some(v)
        })
    }
}

Problems arise when I'm trying to capture "path":

fn iter_sections(&amp;&#39;a self, path: &amp;str) -&gt; Self::FilterMap {
    self.iter().filter_map(|(k,v)| {
        path;  // simulate &quot;path&quot; based filtering
        Some(v)
    }) 
}

Which results in an error at the filter_map() line:

error[E0308]: mismatched types
expected fn pointer, found closure
note: expected fn pointer `for&lt;&#39;r&gt; fn((&amp;&#39;r String, &amp;Section)) -&gt; Option&lt;&amp;Section&gt;`
      found closure `[closure@src/main.rs:31:32: 31:57]`

I don't have any problem returning a FilterMap type parametrized by closure type, but the problem is that this [closure@src/main.rs...] thing doesn't have a type name in Rust.

And that is my question: How do I specify the return type of a trait method that is accepting a parametrized filter so I get a working iter_sections() method?

My attempts to work around this issue so far:

  1. I thought that maybe using a non-parametric function pointer would resolve the issue:
fn make_filter&lt;&#39;a&gt;(path: &amp;&#39;static str) -&gt; impl Fn((&amp;String,
                                                   &amp;&#39;a Section))-&gt;Option&lt;&amp;&#39;a Section&gt; {
    move|(p, c)| {
        path;  // simulate &quot;path&quot; based filtering
        Some(c)
    }
}

used as:

    fn iter_sections(&amp;&#39;a self, path: &amp;&#39;static str) -&gt; Self::FilterMap {
        let filter = make_filter(path);
        self.iter().filter_map(filter)
    }

Which doesn't help:

error[E0308]: mismatched types
expected fn pointer, found opaque type
note: expected fn pointer `for&lt;&#39;r&gt; fn((&amp;&#39;r String, &amp;Section)) -&gt; Option&lt;&amp;Section&gt;`
      found opaque type `impl for&lt;&#39;r&gt; Fn&lt;((&amp;&#39;r String, &amp;Section),)&gt;`
  1. I attempted to return an "impl" type in the trait implementation:
type FilterMap = core::iter::FilterMap&lt;hash_map::Iter&lt;&#39;a, String, Section&gt;,
                                       impl Fn((&amp;String, &amp;&#39;a Section))-&gt;Option&lt;&amp;&#39;a Section&gt;&gt;;

Which apparently isn't possible in stable Rust:

error[E0658]: `impl Trait` in type aliases is unstable

note: see issue #63063 &lt;https://github.com/rust-lang/rust/issues/63063&gt; for more information

I have Rust v1.48 so this is not an option for me.
I know about "Box" based solutions (make the trait deal in Box&lt;dyn Trait&gt; return values) but I'd like to avoid heap allocations whenever possible, and express the iterator type thru type manipulation.

答案1

得分: 2

以下是翻译好的代码部分:

use std::collections::hash_map::Iter as HmIter;
use std::collections::HashMap;

trait HashMapExt<K, V> {
    fn iter_sections<'a>(&'a self, path: &'a str) -> IterSections<'a, K, V>;
}

struct IterSections<'a, K: 'a, V: 'a> {
    path: &'a str,
    map_iter: HmIter<'a, K, V>,
}

impl<'a, K: 'a, V: 'a> Iterator for IterSections<'a, K, V> {
    type Item = <HmIter::<'a, K, V> as Iterator>::Item;
    fn next(&mut self) -> Option<Self::Item> {
        while let Some(res) = self.map_iter.next() {
            // 模拟过滤
            if self.path == "Hello" {
                return Some(res);
            }
        }
        None
    }
}

impl<K, V> HashMapExt<K, V> for HashMap<K, V> {
    fn iter_sections<'a>(&'a self, path: &'a str) -> IterSections<'a, K, V> {
        IterSections {
            path,
            map_iter: self.iter(),
        }
    }
}

请注意,我已经保留了原始代码中的格式和注释。如果您有任何其他问题或需要进一步帮助,请告诉我。

英文:

You can implement filtering manually.

use std::collections::hash_map::Iter as HmIter;
use std::collections::HashMap;

trait HashMapExt&lt;K, V&gt;{
    fn iter_sections&lt;&#39;a&gt;(&amp;&#39;a self, path: &amp;&#39;a str)-&gt;IterSections&lt;&#39;a, K, V&gt;;
}

struct IterSections&lt;&#39;a, K: &#39;a, V: &#39;a&gt;{
    path: &amp;&#39;a str,
    map_iter: HmIter&lt;&#39;a, K, V&gt;,
}

impl&lt;&#39;a, K: &#39;a, V: &#39;a&gt; Iterator for IterSections&lt;&#39;a, K, V&gt; {
    type Item = &lt;HmIter::&lt;&#39;a, K, V&gt; as Iterator&gt;::Item;
    fn next(&amp;mut self)-&gt;Option&lt;Self::Item&gt; {
        while let Some(res) = self.map_iter.next(){
            // Simulate filtering
            if self.path == &quot;Hello&quot; {
                return Some(res);
            }
        }
        None
    }
}

impl&lt;K,V&gt; HashMapExt&lt;K,V&gt; for HashMap&lt;K,V&gt;{
    fn iter_sections&lt;&#39;a&gt;(&amp;&#39;a self, path: &amp;&#39;a str)-&gt;IterSections&lt;&#39;a, K, V&gt;{
        IterSections{
            path,
            map_iter: self.iter(),
        }
    }
}

Probably, you should also implement fold/try_fold functions too.

huangapple
  • 本文由 发表于 2023年3月7日 21:02:28
  • 转载请务必保留本文链接:https://go.coder-hub.com/75662327.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定