由于关联的类型而导致错误的推断寿命 [英] Wrong inferred lifetime due to associated type

查看:84
本文介绍了由于关联的类型而导致错误的推断寿命的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

The following code sample is a minified version of a problem I have.

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关联类型的代码.我试图概括我的代码,以便它可以与其他类型的偏移量一起使用.但是,添加此关联类型导致许多现有代码停止编译,并出现如下错误:

I currently have the code working using usize instead of the Offset associated type. I'm trying to generalize my code so it can work with other types for offset. However, adding this associated type has caused lots of existing code to stop compiling with errors like this:

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的顺序可以解决此示例的问题,但我不想在所有地方(并且可能无法)进行此更改,并且我也不明白为什么这是一个问题.

Reversing the order of header1 and buf2 fixes the problem for this example, but I don't want to have to make this change everywhere (and may not be able to), and I don't understand why it is a problem.

推荐答案

原因

差异是问题的原因.

  • struct Header<R: Reader>(R, usize);中,Header<R>协变. R.
  • 但是,在struct Header<R: Reader>(R, R::Offset);中,Header<R>不变的. R.
  • In struct Header<R: Reader>(R, usize);, Header<R> is covariant w.r.t. R.
  • However, in struct Header<R: Reader>(R, R::Offset);, Header<R> is invariant w.r.t. R.

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

Subtyping is a safe conversion of lifetimes. For example, &'static [u8] can be converted to &'a [u8].

差异描述了如何将子类型提升为复杂类型.例如,如果Header<_>是协变的,并且RS的子类型,则Header<R>Header<S>的子类型.不变结构不是这种情况.

Variance describes how subtyping is lifted to complex types. For example, if Header<_> is covariant and R is a subtype of S, Header<R> is a subtype of Header<S>. This is not the case with invariant structs.

在当前的Rust中,特征始终是不变的,因为无法推断或无法在当前语法中指定特征方差.相同的限制适用于诸如R::Offset的投影类型.

In current Rust, traits are always invariant, because trait variance can't be inferred nor specified in the current syntax. Same restrictions apply to projected types like R::Offset.

在您的代码中,由于Header是不变的,因此即使'a: 'b,也无法将Header<&'a [u8]>上载到Header<&'b [u8]>.由于fn test要求两个参数都具有相同的类型,因此编译器要求slice1slice2具有相同的生存期.

In your code, since Header is invariant, Header<&'a [u8]> can't be upcasted to Header<&'b [u8]> even if 'a: 'b. Since fn test requires the same type for both arguments, the compiler required the same lifetime for slice1 and slice2.

如果可行的话,一种可能的即席解决方案是对fn test的签名进行概括.

One possible ad-hoc solution is to generalize the signature for fn test, if it is feasible.

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

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

Another solution is to make Header covariant somehow.

如果type Offset具有'static边界,也许可以安全地假设Header为协变,但是当前的编译器没有这么聪明的推论.

Maybe it is safe to assume Header to be covariant if type Offset has 'static bound, but the current compiler doesn't do such a clever inference.

也许您可以将生存期拆分为Header的参数.这样可以恢复协方差.

Perhaps you can split out lifetimes as a parameter for Header. This recovers covariance.

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

这篇关于由于关联的类型而导致错误的推断寿命的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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