如何为&amp; T的所有迭代实现一次特征(例如Vec <t>和&amp; [T])

时间:2018-01-08 08:48:27

标签: generics vector iterator rust slice

我有一个特征方法,通过线性扫描其元素来找到对集合中元素的引用。

我希望能够为Vec<Tag>&'a [Tag]实现一次(并且理想情况下也支持其他可迭代数据结构)。

在下面的代码中,TagFinder的实例与Vec<Tag>&'a [Tag]完全相同,但我无法找到一种方法来表达这一点。有可能吗?

这个other question似乎很相关,但我在这里有一个额外的间接层,因为我正在处理“iterables”而不是迭代器。

相关地,如果像IntoIterator这样的特征暴露了引用的迭代器(即Vec<T>&[T]都会迭代&T,那么它似乎会很方便。 ,而不是Vec<T>暴露一个拥有的迭代器)。我不确定为什么不存在这样的事情。

struct Tag {
    key: String,
    value: String,
}

trait TagFinder {
    fn find_tag(&self, key: &str) -> Option<&str>;
}

impl<'a> TagFinder for &'a [Tag] {
    fn find_tag(&self, key: &str) -> Option<&str> {
        find_tag(self.into_iter(), key)
    }
}

impl TagFinder for Vec<Tag> {
    fn find_tag(&self, key: &str) -> Option<&str> {
        find_tag(self.into_iter(), key)
    }
}

fn find_tag<'a, I>(tags: I, key: &str) -> Option<&'a str>
where
    I: Iterator<Item = &'a Tag>,
{
    tags.filter_map(|tag| match tag {
        &Tag {
            key: ref k,
            value: ref v,
        } if k == key =>
        {
            Some(v as &str)
        }
        _ => None,
    }).next()
}

fn main() {
    let v = vec![
        Tag {
            key: "a".to_owned(),
            value: "1".to_owned(),
        },
        Tag {
            key: "b".to_owned(),
            value: "2".to_owned(),
        },
    ];

    let s: &[Tag] = &v;

    assert!(v.find_tag("b") == Some("2"));
    assert!(s.find_tag("b") == Some("2"));
}

修改

经过一些游戏,我想出了以下内容。它可以工作,但我对为什么无效感到满意。

  1. 该特征现在消耗self,除了IntoIterator<Item = &'a Tag>的唯一实施者似乎是借用类型这一事实外,这一点根本不可取,因此{{1}被破坏的只是一个参考。我有点小心,因为没有任何东西(除了惯例)阻止有人为self这样的拥有类型实现这一点。

  2. 将life参数从方法(省略)移动到特征是很奇怪的。我发现很难理解返回值如何以合理的生命周期结束。

  3. 为什么Vec有效?这里的接收者是v.find_tag(...)而不是参考。 Rust如何将其转换为引用?

  4. 感谢。 :)

    Vec

1 个答案:

答案 0 :(得分:3)

  

如何为&T

的所有迭代实现一次特征

就像你指定的那样:

trait Foo {}

impl<'a, T: 'a, I> Foo for I
where
    I: Iterator<Item = &'a T>,
{
}

如果您愿意,可以将IntoIterator替换为Iterator

针对您的具体情况:

trait TagFinder<'a> {
    fn find_tag(self, key: &str) -> Option<&'a str>;
}

impl<'a, I> TagFinder<'a> for I
where
    I: IntoIterator<Item = &'a Tag>,
{
    fn find_tag(self, key: &str) -> Option<&'a str> {
        self.into_iter()
            .filter(|tag| tag.key == key)
            .map(|tag| tag.value.as_ref())
            .next()
    }
}
  

该特征现在消耗self,除了IntoIterator<Item = &'a Tag>的唯一实施者似乎是借用类型这样的事实之外,这一点根本不可取,因此selfVec被摧毁只是一个参考。我有点谨慎,因为没有任何东西(除了惯例)阻止有人为'a这样的拥有类型实现这一点。

如果你能找到某种方式来获取拥有价值并返回它的参考,那么你就发现了Rust的内存安全性的一个关键漏洞。请参阅Is there any way to return a reference to a variable created in a function?

  

将life参数从方法(省略)移动到特征是很奇怪的。我发现很难理解返回值如何以合理的生命周期结束。

我不明白这种困惑。您已明确指定了生命周期,因此以何种方式不合理?你没有从方法中删除它,你只是将它添加到特征中,因为现在特征必须知道v.find_tag(...)来自&#34;外部&#34;特质本身。

  

为什么Vec有效?这里的接收者是_ = childViewController.view 而不是参考。 Rust如何将其转换为参考?

与调用值时引用的任何其他方法调用相同的方式。见What are Rust's exact auto-dereferencing rules?