通用特征和寿命问题 [英] Problem with generic traits and lifetimes
问题描述
在较大的上下文中,我对通用特征有疑问,然后尝试将其缩小到这个较小的问题.我要具有以下功能:
I had a problem with generic traits in a larger context and try to size it down to this smaller problem. I want to have following function:
fn count<I, S, T>(pattern: T, item:I) -> usize
where
I: Atom,
S: Iterator<Item = I>
T: Atoms<I, S>,
{
pattern.atoms().filter(|i| i == &item).count()
}
该函数应传递两个参数:
The function should be passed two arguments:
-
pattern:Atoms< ...>
具有一个工厂方法,原子返回原子的迭代器 -
item:Atom
,这是应该在原子迭代器中计数的项目
pattern:Atoms<...>
which has a factory method atoms returning an iterator of atomsitem:Atom
which is the item that should be counted in the iterator of atoms
该函数应该在两个参数上都是通用的(约束条件是 pattern.atoms()
必须返回具有与 item
相同类型的项的迭代器):
The function should be generic over both arguments (with the constraint that pattern.atoms()
must return an iterator with items of the same type as item
), e.g.:
count(Atoms<u8, std::str::Bytes<'_>>, u8) -> usize
count(Atoms<char, std::str::Chars<'_>>, char) -> usize
count(Atoms<u8, std::io::Bytes>, u8) -> usize
对于Atom,我尝试了这种方法:
For Atom I tried this approach:
pub trait Atom: Copy + Eq + Ord + Display + Debug {}
impl Atom for char {}
impl Atom for u8 {}
接下来,我开始使用Atoms,首先没有生命周期:
Next I started with Atoms, first without lifetimes:
pub trait Atoms<I, S>
where
S: Iterator<Item = I>,
I: Atom,
{
fn atoms(&self) -> S;
}
我尝试使用 std :: str :: Bytes<'a>
作为迭代器为 str
实现此功能.因此,编译器在原子中错过了生命周期注释&'self
.因此,我使用了终生的方法改进了这种方法,并提出了以下代码:
I tried to implement this for str
using std::str::Bytes<'a>
as iterator. Consequently the compiler misses a lifetime annotation &'a self
in atoms. So I refined this approach with lifetimes and came up with this code:
pub trait Atoms<'a, I, S>
where
S: Iterator<Item = I> + 'a,
I: Atom,
{
fn atoms(&'a self) -> S;
}
impl<'a> Atoms<'a, u8, std::str::Bytes<'a>> for &str {
fn atoms(&'a self) -> std::str::Bytes<'a> {
self.bytes()
}
}
fn count<'a, I, S, T>(pattern: T, item: I) -> usize
where
I: Atom,
S: Iterator<Item = I> + 'a,
T: Atoms<'a, I, S>,
{
pattern.atoms().filter(|i| *i == item).count() //<--- compiler complains
}
现在,编译器抱怨参数类型
T 的寿命可能不够长,...请考虑添加... T:'a
.我不明白这个问题.提示甚至不起作用(随之而来的是另一个终身问题),所以我不知道我的误解是什么.有人可以帮助我了解(甚至解决)此问题吗?
Now the compiler complains the parameter type
T may not live long enough, ... consider adding ... T:'a
. I do not understand this problem. The hint does not even work (another lifetime problem follows) so I have no clue what my misunderstanding is. Can someone help me to understand (or even fix) this problem?
推荐答案
atoms
函数需要借用 self
,生命期为'a 代码>.如果为该函数指定的类型寿命不长,则此函数将无法正常工作.
The atoms
function requires self
to be borrowed for a lifetime of 'a
. If you give to this function a type that does not live that long, this function cannot work properly.
例如,类型&'b T
的生存期为'b
.如果为&'b T
实现 Atoms
,并且'b
比'a
短,则必须无法调用 atoms
函数.这就是为什么您必须限制 T
的生存时间至少与'a
一样长.
For example, the type &'b T
has a lifetime of 'b
. If you implement Atoms
for &'b T
and that 'b
is shorter that 'a
, you must not be able to call the atoms
function. This is why you must constrain T
to live at least as long as 'a
.
Rust知道这一点,并自动对 atoms
函数执行此操作:
Rust knows this and automatically do this to the atoms
function:
fn atoms(&'a self) -> std::str::Chars<'a>
where
Self: 'a,
{ self.chars() }
但是,当您尝试在另一个函数中使用该函数时,Rust需要您自己添加此绑定.这就是为什么需要为 count
函数添加它的原因.
But when you try to use that function inside of another function, Rust needs you to add this bond yourself. This is why you need to add it for the count
function.
您的 Atom
特性不需要保留'a
寿命.您的第一种方法是正确的方法.
Your Atom
trait does not need to carry the 'a
lifetime. Your first approach was the right one.
pub trait Atoms<S>
where
S: Iterator,
S::Item: Atom,
{
fn atoms(&self) -> S;
}
(请注意,我删除了通用参数 I
,并将其替换为 S :: Item
.这是可能的,因为 Item
是 Iterator
的关联类型.除了更清晰以外,没有任何改变.)
(note that I removed the generic parameter I
and replaced it with S::Item
. This is possible because Item
is an associated type of Iterator
. That changes nothing apart from being clearer.)
在那之后,实现非常简单.
After that, the implementations are pretty straightforward.
// Note the `&'a str` here, this was missing on your implementation but it is actually
// needed. If you think about it, the bytes that are returned are part of the `&str`
// so they must live at least as long as the reference.
impl<'a> Atoms<std::str::Bytes<'a>> for &'a str {
// The difference is here. You don't need to chose a lifetime for `&self`.
// A longer lifetime than `'a` would be ok. That would be a reference to
// a reference of a lifetime of `'a`.
//
// `self` is `&'a str` here.
// so `&self` is `&&'a str`
//
// What you did was telling Rust that that second reference needed to live
// as long as `'a`. Meaning `&'a &'a str`. But this was wrong. That reference
// must be able to live longer than `'a`.
fn atoms(&self) -> std::str::Bytes<'a> {
self.bytes()
}
}
之后, count
函数将按预期工作:
After that, the count
function work just as intended:
fn count<S, T>(pattern: T, item: I) -> usize
where
S::Item: Atom,
S: Iterator,
T: Atoms<S>,
{
pattern.atoms().filter(|i| *i == item).count() //<--- compiler does not complain anymore ;)
}
这篇关于通用特征和寿命问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!