jpernst / rental

Rust macro to generate self-referential structs
Apache License 2.0
211 stars 11 forks source link

Lifetime error when using covariant attribute on struct that might be covariant #45

Closed wezm closed 4 years ago

wezm commented 4 years ago

I've recently been working on adding rental to allsorts in order to be able to hold font table data alongside its parsed representation in the same struct. I have it working with #[rental] but with #[rental(covariant)] a lifetime error is raised for one of the structs. It's possible that the compiler is identifying an actual issue but my colleagues are a bit confused as to why it's being rejected.

I've extracted the code and reduced it to a minimal example as much as possible here: https://github.com/wezm/rental-covariant-lifetime-param

It appears to be something about ReadArray that is an issue, as the other example struct works as expected. Any insight would be most appreciated.

The error is:

   Compiling rental-covariant-lifetime-param v0.1.0 (/home/wmoore/Work/rental-covariant-lifetime-param)
error[E0308]: mismatched types
  --> src/lib.rs:14:1
   |
14 | / rental! {
15 | |     mod tables {
16 | |         use super::*;
17 | |
...  |
23 | |     }
24 | | }
   | |_^ lifetime mismatch
   |
   = note: expected struct `std::marker::PhantomData>`
              found struct `std::marker::PhantomData>`
note: the lifetime `'__s` as defined on the method body at 6:37...
  --> src/lib.rs:14:1
   |
14 | / rental! {
15 | |     mod tables {
16 | |         use super::*;
17 | |
...  |
23 | |     }
24 | | }
   | |_^
   = note: ...does not necessarily outlive the static lifetime
   = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)

error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'data` due to conflicting requirements
  --> src/lib.rs:14:1
   |
14 | / rental! {
15 | |     mod tables {
16 | |         use super::*;
17 | |
...  |
23 | |     }
24 | | }
   | |_^
   |
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 6:37...
  --> src/lib.rs:14:1
   |
14 | / rental! {
15 | |     mod tables {
16 | |         use super::*;
17 | |
...  |
23 | |     }
24 | | }
   | |_^
note: ...so that the types are compatible
  --> src/lib.rs:14:1
   |
14 | / rental! {
15 | |     mod tables {
16 | |         use super::*;
17 | |
...  |
23 | |     }
24 | | }
   | |_^
   = note: expected  `rental::__rental_prelude::Rental2<'_, '_>`
              found  `rental::__rental_prelude::Rental2<'_, '_>`
   = note: but, the lifetime must be valid for the static lifetime...
note: ...so that the expression is assignable
  --> src/lib.rs:14:1
   |
14 | / rental! {
15 | |     mod tables {
16 | |         use super::*;
17 | |
...  |
23 | |     }
24 | | }
   | |_^
   = note: expected  `&TestTable<'_>`
              found  `&TestTable<'static>`
   = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)

error: aborting due to 2 previous errors
jpernst commented 4 years ago

Taking a quick look, it's probably the ReadCtxt param in ReadBinaryDep's read_dep method that's causing it. The lifetime 'a appears in a type behind a mutable reference, which according to rust's variance rules, forces the lifetime 'a to be invariant, which will break rental's covariant feature. In the working example, the lifetime 'a appears only behind a shared reference, allowing it to remain covariant.

wezm commented 4 years ago

Thanks for taking a look and quick reply. Your insights are helpful. My colleagues and I were a little surprised that a method on a trait might be able influence the variance of the struct, even when the mutable reference supplied to the method is not self. Nonetheless is it your understanding that this can be the case?

jpernst commented 4 years ago

The variance of a struct or trait is determined solely by any lifetime parameters it takes. If 'a is used in an invariant context, it must be invariant, making the trait invariant over that lifetime, and any structs that implement that trait invariant over that lifetime, and so on. self has no special meaning or bearing with respect to lifetime variance.

wezm commented 4 years ago

Great, thanks for the explanation, it helps.