【问题标题】:How to implement trait once for all iterables of &T (e.g. Vec<T> and &[T])如何为 &T 的所有迭代实现一次 trait(例如 Vec<T> 和 &[T])
【发布时间】:2018-06-17 05:25:08
【问题描述】:

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

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

在下面的代码中,TagFinder 的实例对于 Vec&lt;Tag&gt;&amp;'a [Tag] 的实现是相同的,但我找不到通用的表达方式。有可能吗?

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

相关地,如果有像IntoIterator 这样的特征暴露引用的迭代器(即Vec&lt;T&gt;&amp;[T] 都将迭代&amp;T,而不是Vec&lt;T&gt; 暴露一个拥有迭代器)。我不知道为什么这样的东西不存在。

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&lt;Item = &amp;'a Tag&gt;的唯一实现者似乎是借用类型,所以被销毁的self只是一个参考。我有点担心,因为没有什么(除了约定)阻止某人为 Vec 这样的拥有类型实现它。

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

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

谢谢。 :)

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

impl<'a, T> TagFinder<'a> for T
where
    T: IntoIterator<Item = &'a Tag>,
{
    fn find_tag(self, key: &str) -> Option<&'a str> {
        find_tag(self.into_iter(), key)
    }
}

【问题讨论】:

  • T: IntoIterator&lt;Item=&amp;Tag&gt;?你可能不得不摆弄生命周期。
  • @SebastianRedl 我之前尝试过,但你激励我回到它。查看我的编辑。
  • 你应该把你的编辑作为答案
  • @Boiethios 我有点希望有人能给出更好的答案,或者至少对这些观点发表评论。

标签: generics vector iterator rust slice


【解决方案1】:

如何为&amp;T的所有迭代实现一次trait

与您指定的差不多:

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&lt;Item = &amp;'a Tag&gt; 的唯一实现者似乎是借用类型,因此被销毁的self 只是一个引用。我有点担心,因为没有什么(除了约定)阻止某人为像Vec 这样的拥有类型实现它。

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

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

我不明白这种混乱。您已经明确指定了生命周期,那么以何种方式不合理?您没有从方法中删除它,只是将其添加到特征中,因为现在特征必须知道 'a 是特征本身“外部”的东西。

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

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

【讨论】:

  • 感谢您的跟进。关于(1)的好点 - 我没有考虑到这一点。关于 (2),在原始公式中,&amp;selfOption&lt;&amp;str&gt; 的生命周期直接相关(尽管通过省略)。在新的公式中,我的直觉是任何有用的self 类型都将包含对'a 的引用,因此与Option&lt;&amp;'a str&gt; 具有相同的生命周期,但我不确定如何将其形式化。关于(3),有没有办法让编译器转储一个完全合格的 AST,这样我就可以知道它做了什么?特别是,我不确定它使用的是&amp;'a Vec&lt;T&gt; 还是&amp;'a [T] 实例。
  • @BenChallenor (2) 是的,该特征的任何正确实现都将涉及Self 对生命周期'a 的一些引用。我不知道如何为你正式化,但这是第一部分的结果——你不能“想象”一个足够长的参考文献(3)不,但这就是为什么我链接到我所做的问题。它将依次尝试Vec&amp;Vec*Vec,然后是&amp;*Vec,选择第一个实现它的。
  • 谢谢。 (2) 我能想到的最糟糕的情况是通过返回'static 引用来实现() 的特征,但公平地说,您也可以使用原始公式来做到这一点。您当然不能返回密钥&amp;str。所以我想我现在明白了。 (3) 我认为我必须尝试更多示例才能了解自动取消引用规则,但我对这个答案很满意。
  • @BenChallenor 如果迭代器发出'static 引用,那么它将只实现TagFinder&lt;'static&gt;,而不是TagFinder&lt;'a&gt; 任何其他生命周期'a
猜你喜欢
  • 2020-01-22
  • 1970-01-01
  • 2015-05-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多