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

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

我将实现它,而不命名生命周期,因为它们无论如何都会被忽略.然而,在docs example人中,情况是这样的:

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);
}

如果使用生命周期 为'a,则使用生命周期 为'a的方法:

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上指定life参数与直接在方法上指定life参数的区别仅在参数的范围内,因此它可以在整个impl块中使用,而不仅仅是在方法中使用.我错过了什么?

推荐答案

是的,有区别.

该方法的签名与文档中的签名没有区别.

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

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

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

可以看到,deref()被声明为有一个生存期参数.

我认为指定生命周期

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

首先,生命周期 参数与泛型类型参数没有区别.它们使用类似的声明语法并非巧合.与泛型参数一样,生命周期参数也参与方法/函数签名,因此,如果您想要实现一个具有生命周期参数的方法的trait,那么您的实现must也具有相同的生命周期参数(可能的模块重命名).

其次,impl签名中的生命周期参数用来表示不同于函数的生命周期关系.对于方法,由调用者确定他们想要使用的实际生存期参数.同样,它与泛型方法的工作方式类似——调用方可以用所需的任何类型实例化其类型参数.这一点非常重要,尤其是对于Deref,您希望实现Deref的任何内容都可以在调用该方法的引用的生存期内取消引用,而不是其他内容.

然而,对于impl,生命周期参数不是在调用使用该参数的方法时 Select 的,而是在编译器 Select 适当的impl时 Select 的.它可以根据值的类型这样做(通常是这样做的),这就阻止了用户在调用方法时指定任意的生存期.例如:

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

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

在这里,first_two()方法返回一个具有存储在Bytes struct 中的值的生存期的切片.方法的调用者无法决定他们想要的生存期——它总是固定在调用该方法的 struct 中的切片的生存期上.在保持相同语义的情况下,也不可能将life参数下放到方法中,我想您可以理解原因.

在您的情况下,您指定的生存期参数既不参与impl的签名,也不参与任何关联类型,因此可以像在每个函数上单独声明一样使用它(因为调用方法时它可以是任意的),但是,关于方法签名的推理(如上所述)开始了.

Rust相关问答推荐

为什么实例化核心::time::ns不安全?

如何处理动态 struct 实例化?

抽象RUST中的可变/不可变引用

如何为utoipa中的可选查询参数生成OpenAPI模式?

如何使用syn插入 comments ?

为什么实例方法可以像Rust中的静态方法一样被调用?

如何计算迭代器适配器链中过滤的元素的数量

为什么比较Option<;字符串>;具有常数Option<&;str>;需要显式类型转换吗?

Tokio';s io::用Cursor拆分<;Vec<;u8>>;赢得';t get the full writted data

随机函数不返回随机值

如何使用tracing-subscriberRust crate 构建多编写者、全局过滤订阅者

如何使用 Bincode 在 Rust 中序列化 Enum,同时保留 Enum 判别式而不是索引?

Rust 文件未编译到 dll 中

我可以禁用发布模式的开发依赖功能吗?

使用 Rust 从 Raspberry Pi Pico 上的 SPI 读取值

为什么可以在迭代器引用上调用 into_iter?

如何获取包裹在 Arc<> 和 RwLock<> 中的 Rust HashMap<> 的长度?

带有库+多个二进制文件的Cargo 项目,二进制文件由多个文件组成?

BigUint 二进制补码

如果返回类型是通用的,我可以返回 &str 输入的一部分吗?