在 impl 或方法上指定生命周期参数有什么区别? [英] What are the differences between specifying lifetime parameters on an impl or on a method?

查看:45
本文介绍了在 impl 或方法上指定生命周期参数有什么区别?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在 Rust 1.3.0 中,Deref trait 在文档中具有以下签名:

In Rust 1.3.0, the Deref trait has the following signature in the documentation:

pub trait Deref {
    type Target: ?Sized;
    fn deref(&'a self) -> &'a Self::Target;
}

我会在不命名生命周期的情况下实现它,因为它们无论如何都会被忽略.但是,在 docs 示例中看起来像这样:

I would implement it without naming the lifetimes, since they get elided anyway. However, in the docs example it looks like this:

use std::ops::Deref;

struct DerefExample<T> {
    value: T
}

impl<T> Deref for DerefExample<T> {
    type Target = T;

    fn deref<'a>(&'a self) -> &'a T {
        &self.value
    }
}

fn main() {
    let x = DerefExample { value: 'a' };
    assert_eq!('a', *x);
}

这一切都很好,但如果我在 impl 上指定生命周期参数 'a 而不是方法:

This works all well and good, but if I specify the lifetime parameter 'a on the impl instead of the method:

struct DerefExample<T> {
    value: T
}

impl<'a, T> Deref for DerefExample<T> {
    type Target = T;

    fn deref(&'a self) -> &'a T {
        &self.value
    }
}

我收到以下错误:

error[E0308]: method not compatible with trait
  --> src/main.rs:10:5
   |
10 | /     fn deref(&'a self) -> &'a T {
11 | |         &self.value
12 | |     }
   | |_____^ lifetime mismatch
   |
   = note: expected type `fn(&DerefExample<T>) -> &T`
              found type `fn(&'a DerefExample<T>) -> &'a T`
note: the anonymous lifetime #1 defined on the method body at 10:5...
  --> src/main.rs:10:5
   |
10 | /     fn deref(&'a self) -> &'a T {
11 | |         &self.value
12 | |     }
   | |_____^
note: ...does not necessarily outlive the lifetime 'a as defined on the impl at 7:1
  --> src/main.rs:7:1
   |
7  | / impl<'a, T> Deref for DerefExample<T> {
8  | |     type Target = T;
9  | |
10 | |     fn deref(&'a self) -> &'a T {
11 | |         &self.value
12 | |     }
13 | | }
   | |_^

这让我很困惑.该方法的签名与文档中的签名没有什么不同.另外,我认为在impl上指定lifetime参数和直接在方法上的区别只在参数的范围内,所以可以在整个impl块中使用,而不仅仅是方法.我在这里错过了什么?

This confuses me. The method's signature is no different than the one from the docs. In addition, I thought that the difference between specifying the lifetime parameter on the impl or on the method directly is in the scope of the parameter only, so it can be used in the entire impl block instead of just the method. What am I missing here?

推荐答案

是的,有区别.

该方法的签名与文档中的签名没有什么不同.

The method's signature is no different than the one from the docs.

它在文档中看起来像这样的事实是 rustdoc 的错误,并且已经解决了.

The fact that it looks like this in docs is a fault of rustdoc, and has since been resolved.

如果你点击文档右上角的[src]链接,你将被重定向到Deref的实际来源,如下所示(我已经删除了额外的属性和注释):

If you press [src] link in the upper right corner of the documentation, you will be redirected to the actual source of Deref, which looks as follows (I've removed extra attributes and comments):

pub trait Deref {
    type Target: ?Sized;
    fn deref<'a>(&'a self) -> &'a Self::Target;
}

你可以看到 deref() 被声明为有一个生命周期参数.

You can see that deref() is declared to have a lifetime parameter.

我认为指定生命周期的区别impl 上的参数或直接在方法上的参数都在仅参数.

I thought that the difference between specifying the lifetime parameter on the impl or on the method directly is in the scope of the parameter only.

这是错误的.区别不仅仅在于范围.我认为我无法提供令人信服的并排示例,其中语义差异是可见的,但请考虑以下推理.

And this is wrong. The difference is not in scope only. I don't think I will be able to provide convincing side-by-side examples where a semantic difference is visible, but consider the following reasoning.

首先,生命周期参数与泛型类型参数没有区别.它们使用类似的声明语法并非巧合.像泛型参数一样,生命周期参数参与方法/函数签名,所以如果你想实现一个带有生命周期参数的方法的特征,你的实现必须也有相同的生命周期参数(取模可能重命名).

First, lifetime parameters are no different from generic type parameters. It is no coincidence that they use similar declaration syntax. Like generic parameters, lifetime parameters participate in the method/function signature, so if you want to implement a trait which has a method with lifetime parameters, your implementation must have the same lifetime parameters as well (modulo possible renaming).

其次,impl 签名中的生命周期参数用于表达与函数不同的生命周期关系.对于方法,调用者总是决定他们想要使用的实际生命周期参数.同样,它类似于泛型方法的工作方式——调用者可以用他们需要的任何类型实例化它的类型参数.这非常重要,特别是对于 Deref - 您希望任何实现 Deref 的东西都可以在调用该方法的引用的生命周期内解除引用,而不是其他任何东西.

Second, lifetime parameters in impl signature are used to express different kinds of lifetime relationship than those on functions. For methods, it is always the caller who determines the actual lifetime parameter they want to use. It is, again, similar to how generic methods work - the caller may instantiate its type parameters with any type they need. It is very important, for Deref in particular - you would want that anything which implements Deref may be dereferenced with the lifetime of the reference the method is called on, not something else.

使用impl,但是,生命周期参数不是在调用使用此参数的方法时选择的,而是在编译器选择适当的impl 时选择的.它可能会根据值的类型这样做(并且通常会这样做),这会阻止用户在调用方法时指定任意生命周期.例如:

With impl, however, lifetime parameters are chosen not when the method which uses this parameter is called, but when the appropriate impl is chosen by the compiler. It may do so (and usually does so) based on the type of the value, which precludes the user from specifying arbitrary lifetimes when the method is called. For example:

struct Bytes<'a>(&'a [u8]);

impl<'a> Bytes<'a> {
    fn first_two(&self) -> &'a [u8] {
        &self.0[..2]
    }
}

此处,first_two() 方法返回一个切片,该切片具有存储在 Bytes 结构中的值的生命周期.方法的调用者无法决定他们想要哪个生命周期——它总是固定在调用这个方法的结构内的切片的生命周期.也不可能在保持相同语义的情况下将生命周期参数带到方法中,我想你明白为什么了.

Here, the first_two() method returns a slice with a lifetime of the value which is stored inside the Bytes structure. The caller of the method can't decide which lifetime they want - it is always fixed to the lifetime of the slice inside the structure this method is called on. It is also impossible to bring the lifetime parameter down to the method while keeping the same semantics, I guess you can see why.

在您的情况下,您指定的生命周期参数既不参与 impl 的签名,也不参与任何关联的类型,因此理论上它可以被用作分别在每个函数上声明(因为在调用方法时它可以是任意的),但是关于方法签名的推理(上面提供)开始了.

In your case the lifetime parameter you specified does not participate either in the signature of the impl nor in any associated types, so it theoretically could be used as if it was declared on each function separately (because it can be arbitrary when the method is called), but then the reasoning about method signatures (provided above) kicks in.

这篇关于在 impl 或方法上指定生命周期参数有什么区别?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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