Incorrect expected lifetime due to associated type

The following code example is a shortened version of the 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); } 

I am currently using code using usize instead of the associated Offset type. I am trying to generalize my code so that it can work with other types for offset. However, adding this related type caused a lot of existing code to stop compiling with these errors:

 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 

Reversing the order of header1 and buf2 fixes the problem for this example, but I don't want this change to happen everywhere (and maybe it can't), and I don't understand why this is the problem.

+5
source share
1 answer

Cause

Deviation is the cause of the problem.

  • In struct Header<R: Reader>(R, usize); , Header<R> covariant wrt R
  • However, in struct Header<R: Reader>(R, R::Offset); , Header<R> is an invariant wrt R

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

The difference describes how subtyping rises 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 does not apply to invariant structures.

In the current Rust, attributes are always invariant , since the variance of the attribute cannot be inferred or not specified in the current syntax. The same restrictions apply to projected types, such as R::Offset .

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

Decision

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

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

Another solution is to make Header covariance somehow.

Perhaps it is safe to assume that the Header is covariant if type Offset has 'static , but the current compiler does not make such smart output.

Perhaps you can split the lifetime as a parameter for the Header . This restores 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); } 
+5
source

Source: https://habr.com/ru/post/1270135/


All Articles