简体   繁体   English

如何对&T的所有可迭代对象实施一次特征(例如,Vec <T> 和T])

[英]How to implement trait once for all iterables of &T (e.g. Vec<T> and &[T])

I have a trait method that finds a reference to an element in a collection by linearly scanning through its elements. 我有一个trait方法,该方法通过线性扫描元素的集合来查找对集合中元素的引用。

I'd like to be able to implement this once for both Vec<Tag> and &'a [Tag] (and ideally support other iterable data structures too). 我希望能够对Vec<Tag>&'a [Tag]都实现一次(并且最好也支持其他可迭代的数据结构)。

In the code below, the instances of TagFinder are identically implemented for Vec<Tag> and &'a [Tag] , but I can't find a way to express this generically. 在下面的代码中,为Vec<Tag>&'a [Tag]完全实现了TagFinder的实例,但是我找不到一种通用的表达方式。 Is it possible? 可能吗?

This other question seems relevant, but I have an extra level of indirection here in that I'm dealing with "iterables" and not iterators. 另一个问题似乎很相关,但是在这里我要处理的是“可迭代对象”,而不是迭代器,因此在这里具有更高的间接性。

Relatedly, it seems it would be handy if there were a trait like IntoIterator that exposed an iterator of references (ie Vec<T> and &[T] would both iterate over &T , rather than Vec<T> exposing an owning iterator). 相关地,如果像IntoIterator这样的特征暴露了引用的迭代器,则似乎很方便(即Vec<T>&[T]都将遍历&T ,而不是Vec<T>公开拥有的迭代器)。 I'm not sure why such a thing doesn't exist. 我不知道为什么这样的事情不存在。

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"));
}

Edit 编辑

After some playing around I've come up with the following. 经过一番游戏后,我提出了以下建议。 It works, but I'm not really comfortable with why it works. 它有效,但是我对它为什么起作用并不十分满意。

  1. The trait now consumes self , which would not be at all desirable, except for the fact that the only implementers of IntoIterator<Item = &'a Tag> seem to be borrowing types, so the self that is destroyed is only a reference. 现在,特征消耗了self ,这是完全不希望的,除了IntoIterator<Item = &'a Tag>的唯一实现者似乎是借用的类型,因此被破坏的self只是一个引用。 I'm a bit wary because there is nothing (except convention) stopping someone implementing that for an owning type like Vec . 我有点警惕,因为没有什么(除约定外)阻止某人为诸如Vec类的拥有类型实现它。

  2. Moving the lifetime parameter from the method (elided) to the trait is weird. 将生命周期参数从方法(消除)移至特征是很奇怪的。 I'm finding it hard to understand how the return value ends up with a sensible lifetime. 我发现很难理解返回值如何以合理的寿命结束。

  3. Why does v.find_tag(...) work? 为什么v.find_tag(...)有效? The receiver here is a Vec not a reference. 此处的接收者是Vec而不是参考。 How is Rust converting it to a reference? Rust如何将其转换为参考?

Thanks. 谢谢。 :) :)

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)
    }
}

How to implement trait once for all iterables of &T 如何对&T所有可迭代对象实施一次特征

Pretty much as you've specified: 与您指定的差不多:

trait Foo {}

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

You can substitute IntoIterator for Iterator if you'd like. 您可以根据需要用IntoIterator代替Iterator

For your specific case: 对于您的具体情况:

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()
    }
}

The trait now consumes self , which would not be at all desirable, except for the fact that the only implementers of IntoIterator<Item = &'a Tag> seem to be borrowing types, so the self that is destroyed is only a reference. 现在,特征消耗了self ,这是完全不希望的,除了IntoIterator<Item = &'a Tag>的唯一实现者似乎是借用的类型,因此被破坏的self只是一个引用。 I'm a bit wary because there is nothing (except convention) stopping someone implementing that for an owning type like Vec . 我有点警惕,因为没有什么(除约定外)阻止某人为诸如Vec类的拥有类型实现它。

If you can find some way to take an owning value and return a reference to it, then you've found a critical hole in Rust's memory safety. 如果您找到某种方法来获取一个拥有的值并返回对该值的引用,那么您已经发现Rust的内存安全性存在严重漏洞。 See Is there any way to return a reference to a variable created in a function? 请参见是否有任何方法可以返回对在函数中创建的变量的引用? .

Moving the lifetime parameter from the method (elided) to the trait is weird. 将生命周期参数从方法(消除)移至特征是很奇怪的。 I'm finding it hard to understand how the return value ends up with a sensible lifetime. 我发现很难理解返回值如何以合理的寿命结束。

I don't understand the confusion. 我不明白这个困惑。 You've explicitly specified the lifetime, so in what manner would it not be reasonable? 您已经明确指定了生存期,那么用什么方式不合理? You aren't removing it from the method, you are just adding it to the trait because now the trait has to know that 'a is something from "outside" the trait itself. 您并没有从方法中删除它,而是将其添加到特征中,因为现在特征必须知道'a是特征本身“之外”的东西。

Why does v.find_tag(...) work? 为什么v.find_tag(...)有效? The receiver here is a Vec not a reference. 此处的接收者是Vec而不是参考。 How is Rust converting it to a reference? Rust如何将其转换为参考?

The same way any other method call that takes a reference works when called on a value. 对值进行调用时,采用引用的任何其他方法调用的工作方式相同。 See What are Rust's exact auto-dereferencing rules? 请参阅Rust的确切自动引用规则是什么?

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何为通用类型Vec的向量实现特征 <T> ? - How to implement a trait for a vector of generic type Vec<T>? 如何在 Vec 上将 trait 变成泛型<t> ?</t> - How to make a trait into a generic on Vec<T>? 你如何为 T 实现一个特征,其中 T: Foo OR Bar - How do you implement a trait for T where T: Foo OR Bar 用Java转换通用类型(例如T值=(T)inputParam) - Casting Generic Types in Java (e.g. T value = (T) inputParam) 嵌套时等效的类型的名称(例如,异步 <Async<T> &gt;等同于异步 <T> ) - Name for types that are equivalent when nested (e.g. Async<Async<T>> equivalent to Async<T>) 为什么不能例如List <ChildClass> 传递给一个接受List的方法 <ParentClass> 作为参数? - Why can't e.g. List<ChildClass> be passed to a method that takes a List<ParentClass> as parameter? 转换通用 Span<t> 到一个特定的实例化(例如 Span<int> ) 如果它在运行时实际上是那种类型</int></t> - Converting a generic Span<T> to a specific instantiation (e.g. Span<int>) if it actually of that type at runtime 获取通用类型对象(例如MyObject <T> )从远程机器 - Get generic type object (e.g. MyObject<T>) from remote machine 为什么泛型的类型变量通常被声明为单个字母(例如:<T>)? - Why are type variables for generics commonly declared as a single letter (e.g.: <T>)? Rust 是否执行自<vec<t> &gt; 对于 Vec <u>,如果我已经实现 From</u><t> <u>为了你?</u> </t></vec<t> - Does Rust implement From<Vec<T>> for Vec<U> if I have already implemented From<T> for U?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM