[英]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"));
}
After some playing around I've come up with the following. 经过一番游戏后,我提出了以下建议。 It works, but I'm not really comfortable with why it works.
它有效,但是我对它为什么起作用并不十分满意。
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
类的拥有类型实现它。
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.
我发现很难理解返回值如何以合理的寿命结束。
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 ofIntoIterator<Item = &'a Tag>
seem to be borrowing types, so theself
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 likeVec
.我有点警惕,因为没有什么(除约定外)阻止某人为诸如
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 aVec
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.