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

时间:2017-07-24 05:43:51

标签: rust lifetime

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

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

我目前使用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

撤消header1buf2的顺序修复了此示例的问题,但我不想在任何地方(也可能无法)进行此更改,并且我不明白为什么这是一个问题。

1 个答案:

答案 0 :(得分:5)

原因

Variance是导致问题的原因。

  • struct Header<R: Reader>(R, usize);中,Header<R> 协变 w.r.t. R
  • 但是,在struct Header<R: Reader>(R, R::Offset);中,Header<R> 不变 w.r.t. R

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

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

在当前的Rust中,特征始终是不变的,因为在当前语法中无法推断或指定特征差异。同样的限制适用于R::Offset等预计类型。

在您的代码中,由于Header不变,Header<&'a [u8]>即使Header<&'b [u8]>也无法升级到'a: 'b。由于fn test要求两个参数使用相同的类型,因此编译器需要slice1slice2的相同生命周期。

解决方案

一种可能的临时解决方案是概括fn test的签名,如果可行的话。

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

另一个解决方案是以某种方式使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);
}