由于关联类型导致的推断生命周期错误

Phi*_*aig 5 lifetime rust

以下代码示例是我遇到的问题的缩小版本.

trait Offset: Default {}

trait Reader {
    type Offset: Offset;
}

impl Offset for usize {}

impl<'a> Reader for &'a [u8] {
    type Offset = usize;
}

// OK
// struct Header<R: Reader>(R, usize);

// Bad
struct Header<R: Reader>(R, R::Offset);

impl <R: Reader<Offset=usize>> Header<R> {
    fn new(r: R) -> Self {
        Header(r, 0)
    }
}

fn test<R: Reader>(_: Header<R>, _: Header<R>) {}

fn main() {
    let buf1 = [0u8];
    let slice1 = &buf1[..];
    let header1 = Header::new(slice1);

    let buf2 = [0u8];
    let slice2 = &buf2[..];
    let header2 = Header::new(slice2);

    test(header1, header2);
}
Run Code Online (Sandbox Code Playgroud)

我目前使用代码usize而不是Offset相关的类型.我正在尝试概括我的代码,以便它可以与其他类型的偏移一起使用.但是,添加此关联类型会导致许多现有代码停止编译,并出现以下错误:

error[E0597]: `buf2` does not live long enough
  --> src/main.rs:37:1
   |
33 |     let slice2 = &buf2[..];
   |                   ---- borrow occurs here
...
37 | }
   | ^ `buf2` dropped here while still borrowed
   |
   = note: values in a scope are dropped in the opposite order they are created
Run Code Online (Sandbox Code Playgroud)

颠倒这个例子的顺序header1buf2解决问题,但我不想在任何地方(也可能无法)进行此更改,我不明白为什么这是一个问题.

Mas*_*ara 5

原因

差异是问题的原因.

  • struct Header<R: Reader>(R, usize);,Header<R>协变 wrt R.
  • 但是,在struct Header<R: Reader>(R, R::Offset);,Header<R>不变的 wrt R.

子类型是生命周期的安全转换.例如,&'static [u8]可以转换为&'a [u8].

方差描述了如何将子类型提升到复杂类型.例如,如果Header<_>是协变且R是子类型S,Header<R>则是子类型Header<S>.对于不变结构,情况并非如此.

在当前的Rust中,traits总是不变的,因为在当前语法中无法推断或指定trait方差.同样的限制适用于预计的类型,如R::Offset.

在你的代码,因为Header是不变的,Header<&'a [u8]>不能upcasted到Header<&'b [u8]>即使'a: 'b.由于fn test需要两个参数相同的类型,则编译器需要相同的寿命slice1slice2.

一种可能的临时解决方案是在fn test可行的情况下概括签名.

fn test<R: Reader, S: Reader>(_: Header<R>, _: Header<S>) {}
Run Code Online (Sandbox Code Playgroud)

另一种解决方案是以Header某种方式使协变.

Header如果type Offset已经'static绑定,也许可以安全地假设是协变的,但是当前的编译器不会做这样一个聪明的推理.

也许你可以把生命时间作为一个参数来分解Header.这恢复了协方差.

trait Offset: Default {}

trait Reader {
    type Offset: Offset;
}

impl Offset for usize {}

impl Reader for [u8] {
    type Offset = usize;
}

struct Header<'a, R: Reader + ?Sized + 'a>(&'a R, R::Offset);

impl <'a, R: Reader<Offset=usize> + ?Sized> Header<'a, R> {
    fn new(r: &'a R) -> Self {
        Header(r, 0)
    }
}

fn test<R: Reader + ?Sized>(_: Header<R>, _: Header<R>) {}

fn main() {
    let buf1 = [0u8];
    let slice1 = &buf1[..];
    let header1 = Header::new(slice1);

    let buf2 = [0u8];
    let slice2 = &buf2[..];
    let header2 = Header::new(slice2);

    test(header1, header2);
}
Run Code Online (Sandbox Code Playgroud)