为啥需要使用加号运算符 (Iterator<Item = &Foo> + 'a) 为特征添加生命周期?

Posted

技术标签:

【中文标题】为啥需要使用加号运算符 (Iterator<Item = &Foo> + \'a) 为特征添加生命周期?【英文标题】:Why is adding a lifetime to a trait with the plus operator (Iterator<Item = &Foo> + 'a) needed?为什么需要使用加号运算符 (Iterator<Item = &Foo> + 'a) 为特征添加生命周期? 【发布时间】:2017-06-21 01:46:45 【问题描述】:

我在迭代器上应用了一个闭包,我想使用稳定的,所以我想返回一个装箱的Iterator。这样做的明显方法如下:

struct Foo;

fn into_iterator(myvec: &Vec<Foo>) -> Box<dyn Iterator<Item = &Foo>> 
    Box::new(myvec.iter())

这失败了,因为借用检查器无法推断出适当的生命周期。

经过一番研究,我找到了What is the correct way to return an Iterator (or any other trait)?,这让我添加了+ 'a

fn into_iterator<'a>(myvec: &'a Vec<Foo>) -> Box<dyn Iterator<Item = &'a Foo> + 'a> 
    Box::new(myvec.iter())

但我不明白

这是做什么的 这里为什么需要它

【问题讨论】:

【参考方案1】:

有一件事很容易被忽视:如果你有一个特征 Bar 并且你想要一个装箱的特征对象 Box&lt;dyn Bar&gt;,编译器会自动添加一个 'static 生命周期绑定(如 RFC 599 中指定的那样) )。这意味着Box&lt;dyn Bar&gt;Box&lt;dyn Bar + 'static&gt; 是等价的!​​p>

在您的情况下,编译器会自动添加静态绑定,以便 this ...

fn into_iterator(myvec: &Vec<Foo>) -> Box<dyn Iterator<Item = &Foo>>

... 等价于:

fn into_iterator(myvec: &Vec<Foo>) -> Box<dyn Iterator<Item = &Foo> + 'static>

现在生命周期省略规则启动并“连接”两个生命周期,这样上面的代码就相当于:

fn into_iterator<'a>(myvec: &'a Vec<Foo>) -> Box<dyn Iterator<Item = &'a Foo> + 'static>

但是Iter&lt;'a, Foo&gt;类型(Vec&lt;Foo&gt;的具体迭代器类型)显然不满足绑定'static(因为它借用了Vec&lt;Foo&gt;)!所以我们必须通过指定我们自己的生命周期边界来告诉编译器我们不想绑定默认的'static

fn into_iterator<'a>(myvec: &'a Vec<Foo>) -> Box<dyn Iterator<Item = &Foo> + 'a>

现在编译器知道特征对象仅在生命周期'a 内有效。请注意,我们不需要明确地注释关联 Item 类型的生命周期!终身省略规则可以解决这个问题。

【讨论】:

哦,当然!我完全忘记了这不是关于 Foo 而是 Iterator 本身;我首先认为生命周期绑定在结构上......感谢您澄清这一点!

以上是关于为啥需要使用加号运算符 (Iterator<Item = &Foo> + 'a) 为特征添加生命周期?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 F# 有一个一元加号运算符?

为啥Java在使用“加号”运算符时会执行从双精度到整数的隐式类型转换? [复制]

为啥加号运算符在 JavaScript 的正则表达式中不起作用? [复制]

为啥要一个句号,“。”而不是加号“+”,用于 PHP 中的字符串连接?

运算符重载

为啥我们在赋值运算符重载中使用引用返回而不是在加减运算中?