在Rust < 1.31中,如果生命周期没有被使用,为什么在引用类型上实现trait需要一个生命周期?

14
我正在使用 Rust 1.31 以前的版本为引用类型实现一个 trait。当我告诉 Rust 我要为哪种引用类型实现 trait 时,为什么它需要一个明确的生命周期?
这里有一个简单的例子。一个名为 Inches 的结构体,为 &Inches 实现 Add trait,并编写了一个使用该实现的函数。
初始示例: (Rust playground link)
use std::ops::Add;

struct Inches(i32);

// this would work: impl<'b> Add for &'b Inches
impl Add for &Inches {
    type Output = Inches;

    fn add(self, other: &Inches) -> Inches {
        let &Inches(x) = self;
        let &Inches(y) = other;

        Inches(x + y)
    }
}

// lifetime specifier needed here because otherwise 
// `total = hilt + blade` doesn't know whether `total` should live
// as long as `hilt`, or as long as `blade`.
fn add_inches<'a>(hilt: &'a Inches, blade: &'a Inches) {
    let total = hilt + blade;
    let Inches(t) = total;
    println!("length {}", t);
}

fn main() {
    let hilt = Inches(10);
    let blade = Inches(20);

    add_inches(&hilt, &blade);
}

编译失败,出现以下错误:
error: missing lifetime specifier [E0106]
    impl Add for &Inches {
                 ^~~~~~~

我加入了缺失的生命周期说明符(仍无法编译)。
// was: impl Add for &Inches {
impl Add for &'b Inches {
    ...
}

编译错误:
error: use of undeclared lifetime name `'b` [E0261]
    impl Add for &'b Inches {

我在impl上声明了生命周期(现在已经编译通过)

(Rust playground链接)

// was: impl Add for &'b Inches {
impl<'b> Add for &'b Inches {
    ...
}

This, finally, compiles correctly.
我的问题
为什么在 "impl Add for &Inches" 中的 "&Inches" 被认为缺少生命周期说明符?通过告诉编译器这个 Add 方法是针对具有某个未指定非静态生命周期 'b 的 "&Inches",并且从未在其他任何地方引用该生命周期,解决了什么问题?

1
'b的生命周期在编译时作为'+操作中的'a'被提供,使得编译器可以强制执行/确保'b'对'a'的寿命要求,并将其传递到main函数中的变量中去,对吗?因此,'b'本身并不是未使用的,它表达了调用者(直接或间接)必须遵守的要求。 - Erik Eidt
你的问题已经说明了它适用于哪个版本:我正在使用Rust 1.30为引用类型实现一个特质。 - Shepmaster
1个回答

12

Rust 1.31及以上版本

原因很简单:在Rust 1.31之前,这个功能还未被实现。

现在,最初的示例可以编译,并且您可以编写impl Add for &Inches,而不是impl<'b> Add for &'b Inches。 这是因为1.31.0稳定了新的生命周期省略规则

Rust 1.31之前

如果您查看生命周期省略的RFC,您会发现它应该覆盖您的用例:

impl Reader for BufReader { ... }                       // elided
impl<'a> Reader for BufReader<'a> { .. }                // expanded
然而,我在游乐场里尝试了一下,它不起作用。原因是它还没有被实现
我在 Rust 的源代码中搜索了这种情况,但令人惊讶的是,这种情况非常少。我只能找到本机类型的 Add 这个系列的实现:
impl Add<u8> for u8
impl<'a> Add<u8> for &'a u8
impl<'a> Add<&'a u8> for u8
impl<'a, 'b> Add<&'a u8> for &'b u8

正如您所看到的,这里所有的生命周期都是显式的;没有省略发生。

针对您的具体问题,我认为在 RFC 实现完成之前,您必须坚持使用显式生命周期!


我认为即使使用省略规则省略了生命周期,但概念上仍然存在,必须由调用者提供并予以尊重,对吗? - Erik Eidt
3
生命周期仍然完全存在。省略规则指示编译器如何在编译时解决缺失的生命周期,避免您编写“显而易见”的样板注释。是的,调用者必须遵守这些规则,但反过来,这些调用者可以在其相关代码的部分中受益于省略。 - mdup
好的。所以,OP要问的一个问题是未指定... 'b',我的观点是它不是未指定的,在某种意义上来说,调用者通过传递它进行了指定(实际上是在编译时,并且还可以通过调用方的调用者来传递),而这是独立于文本省略是否起作用的。当然,我可能误解了OP的意思... - Erik Eidt
感谢 @ErikEidt 和 mdup 仔细阅读并回答我的问题。你们的回答 "必须始终存在生命周期,无论是显式还是推断出来的"(Erik)+ "编译器目前无法推断这个"(mdup),共同回答了我问题的两个部分:'为什么要添加生命周期,以及为什么我不能省略它'。非常感谢你们两位。 - Esteis
1
特别感谢您添加RFC和问题链接。指引新人到主要来源和论坛对于教授人们如何寻找答案非常宝贵,同时也有助于吸引更多的人加入社区。 - Esteis

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接