通用特征和寿命问题 [英] Problem with generic traits and lifetimes

查看:51
本文介绍了通用特征和寿命问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在较大的上下文中,我对通用特征有疑问,然后尝试将其缩小到这个较小的问题.我要具有以下功能:

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 atoms
  • item: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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆