在Rust 1.3.0中,Deref
特征在文档中具有以下签名:
pub trait Deref {
type Target: ?Sized;
fn deref(&'a self) -> &'a Self::Target;
}
Run Code Online (Sandbox Code Playgroud)
我会在没有命名生命期的情况下实现它,因为它们无论如何都会被省略.但是,在docs示例中,它看起来像这样:
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);
}
Run Code Online (Sandbox Code Playgroud)
这一切都很好,但是如果我'a
在impl而不是方法上指定了lifetime参数:
struct DerefExample<T> {
value: T
}
impl<'a, T> Deref for DerefExample<T> {
type Target = T;
fn deref(&'a self) -> &'a T {
&self.value
}
}
Run Code Online (Sandbox Code Playgroud)
我收到以下错误:
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 | | }
| |_^
Run Code Online (Sandbox Code Playgroud)
这让我很困惑.该方法的签名与文档中的签名没有什么不同.另外,我认为在impl上或在方法上直接指定life参数之间的区别仅在于参数的范围,因此它可以在整个impl块中使用,而不仅仅是在方法中使用.我在这里错过了什么?
Vla*_*eev 11
是,有一点不同.
该方法的签名与文档中的签名没有什么不同.
在docs中看起来像这样的事实是rustdoc的错误,并且已经解决了.
如果按[src]
文档右上角的链接,您将被重定向到实际的源代码Deref
,如下所示(我删除了额外的属性和注释):
pub trait Deref {
type Target: ?Sized;
fn deref<'a>(&'a self) -> &'a Self::Target;
}
Run Code Online (Sandbox Code Playgroud)
您可以看到deref()
声明具有生命周期参数.
我认为直接在impl或方法上指定lifetime参数之间的区别仅在于参数的范围.
这是错误的.差异不在范围内.我不认为我能够提供令人信服的并排示例,其中可以看到语义差异,但请考虑以下推理.
首先,生命周期参数与泛型类型参数没有区别.他们使用类似的声明语法并非巧合.与泛型参数一样,生命周期参数也参与方法/函数签名,因此如果要实现具有生命周期参数的方法的特征,则实现必须具有相同的生命周期参数(模可能重命名).
其次,impl
签名中的生命周期参数用于表示与函数相关的不同种类的生命关系.对于方法,始终是调用者确定他们想要使用的实际生命周期参数.它再次类似于泛型方法的工作方式 - 调用者可以使用他们需要的任何类型实例化其类型参数.特别是,非常重要的Deref
是 - 您可能希望实现的任何内容都Deref
可以通过调用该方法的引用的生命周期取消引用,而不是其他内容.
用impl
,然而,寿命参数被选择时不使用这种参数的方法被调用,但当适当的impl
是由编译器选择的.它可以根据值的类型(通常这样做)这样做,这使得用户无法在调用方法时指定任意生命周期.例如:
struct Bytes<'a>(&'a [u8]);
impl<'a> Bytes<'a> {
fn first_two(&self) -> &'a [u8] {
&self.0[..2]
}
}
Run Code Online (Sandbox Code Playgroud)
这里,该first_two()
方法返回具有存储在Bytes
结构内部的值的生存期的切片.方法的调用者无法决定他们想要的生命周期 - 它始终固定在调用此方法的结构内的切片的生命周期内.在保持相同的语义的同时,也不可能将life参数降低到方法,我想你可以看到原因.
在您的情况下,您指定的生命周期参数既不参与impl
也不在任何关联类型的签名中,因此理论上可以使用它,就像它在每个函数上单独声明一样(因为它在调用方法时可以是任意的) ,但随后关于方法签名(上面提供)的推理开始了.