Cannot Infer Appropriate Lifetime

After having fixed my previous issue, I encountered the next one and I cannot wrap my head around how to fix this. I suspect I have to change some lifetimes, i.e. I cannot put 'a everywhere and assume it's correct, but the error message isn't very helpful in this case. I've simplified the code as much as I could, but that didn't help me find a solution, so far.

What do I have to do to be able to loop over the references in the drop method?

use ::std::marker::PhantomData;

trait Collection<'a, Element>
where
    Self: 'a,
    &'a Self: IntoIterator<Item = &'a Element>,
    Element: 'a,
{
}

struct MyStruct<'a, Collection>
where
    Collection: self::Collection<'a, ()>,
    &'a Collection: IntoIterator<Item = &'a ()>,
{
    references: Collection,
    _data: PhantomData<&'a ()>,
}

impl<'a, Collection> Drop for MyStruct<'a, Collection>
where
    Collection: self::Collection<'a, ()>,
    &'a Collection: IntoIterator<Item = &'a ()>,
{
    fn drop(&mut self) {
        for reference in &self.references {}
    }
}

fn main() {}

(Playground)

Errors:

   Compiling playground v0.0.1 (/playground)
error[E0495]: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements
  --> src/lib.rs:26:26
   |
26 |         for reference in &self.references {}
   |                          ^^^^^^^^^^^^^^^^
   |
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 25:5...
  --> src/lib.rs:25:5
   |
25 | /     fn drop(&mut self) {
26 | |         for reference in &self.references {}
27 | |     }
   | |_____^
note: ...so that reference does not outlive borrowed content
  --> src/lib.rs:26:26
   |
26 |         for reference in &self.references {}
   |                          ^^^^^^^^^^^^^^^^
note: but, the lifetime must be valid for the lifetime 'a as defined on the impl at 20:6...
  --> src/lib.rs:20:6
   |
20 | impl<'a, Collection> Drop for MyStruct<'a, Collection>
   |      ^^
   = note: ...so that the types are compatible:
           expected std::iter::IntoIterator
              found std::iter::IntoIterator

error: aborting due to previous error

error: could not compile `playground`.

To learn more, run the command again with --verbose.

What are you trying to achieve here?

Random thoughts:

  • Don't mix trait names with parameter names. Using Collection for both makes the code confusing. Sadly, Rust doesn't have a great syntax and usually parameters get 1-letter names.

  • When you have Trait<'a> or Struct<'a> it means the thing marked by 'a must have been already borrowed before the struct has been created. It means you can't borrow it later! You need to have the borrow first, then create the struct. This is what the error is about: &self.references makes a new borrow later, after self. already existed.

How about this? The trick here is that references are copyable.

use ::std::marker::PhantomData;

struct MyStruct<C, E> where C: IntoIterator<Item = E> + Copy {
    references: C,
    _m: PhantomData<E>,
}

impl<C, E> Drop for MyStruct<C, E> where C: IntoIterator<Item = E> + Copy
{
    fn drop(&mut self) {
        for _ in self.references {}
    }
}

fn main() {
    MyStruct {
        references: &[1,2,3],
        _m: PhantomData,
    };
}

A collection should be iterable in all forms, i.e. you should be able to iterate it whether you own it or you have a shared or mutually exclusive reference to it. For example, Vec does the same by implementing IntoIterator<Item = T>, but also IntoIterator<Item = &'a T> for &'a Vec and IntoIterator<Item = &'a mut T> for &'a mut Vec. It's just more common to use Slice::iter and Slice::iter_mut for convenience, because Vec implements Deref<[T]>, but for any generic iterable collection, as in my case, you must implement IntoIter 3 times. There are no standard library traits, that add iter() and iter_mut().

What I want to achieve is, that MyStruct can be used with any kind of collection. The trait has been dumbed down for this example, but in its original form it also contains the methods add, remove and contains. The original implementation of MyStruct contains a collection, that contains references of other MyStructs. If instance A has a reference to instance B, then B has a reference to A. Due to these cyclic references, I must implement Drop to remove references to the instance itself, that will be dangling after drop has completed.

I'm not sure if Rust supports that level of abstraction.

This breaks down:

use ::std::marker::PhantomData;

struct MyStruct<C, E> where for<'a> &'a C: IntoIterator<Item = E> {
    references: C,
    _m: PhantomData<E>,
}

impl<C, E> Drop for MyStruct<C, E> where for<'a> &'a C: IntoIterator<Item = E> {
    fn drop(&mut self) {
        for _ in &self.references {}
    }
}

fn main() {
    MyStruct {
        references: vec![1,2,3],
        _m: PhantomData,
    };
}

even though this works:

use ::std::marker::PhantomData;

struct MyStruct<C, E> {
    references: C,
    _m: PhantomData<E>,
}

impl<C, E> MyStruct<C, E> where for<'a> &'a C: IntoIterator<Item = E> {
    fn blah(&mut self) {
        for _ in &self.references {}
    }
}

fn main() {
    MyStruct {
        references: vec![1,2,3],
        _m: PhantomData::<i32>,
    };
}

BTW: <'a> means borrowing must have happened earlier. for<'a> &'a allows borrowing at any time.

Thank you for the insight. You also made me realize, that I can reduce the example even further by getting rid of the trait. Then I thought, why even bother with my own struct to show the error and I made another trait with a blank implementation. I also got rid of Drop and just made a regular implementation. From there I figured out how this problem could be solved, if it wasn't the drop method:

trait Collection<'a>
where
    Self: 'a,
    &'a Self: IntoIterator,
    <&'a Self as IntoIterator>::Item: core::fmt::Debug,
{
    fn do_something(&'a self) {
        for reference in self {
            dbg!(reference);
        }
    }
}

impl<'a, T: 'a> Collection<'a> for T
where
    &'a T: IntoIterator,
    <&'a T as IntoIterator>::Item: core::fmt::Debug,
{
}

fn main() {
    vec!["hello", "world"].do_something();
}

(Playground)

Errors:

   Compiling playground v0.0.1 (/playground)
    Finished dev [unoptimized + debuginfo] target(s) in 0.48s
     Running `target/debug/playground`
[src/main.rs:9] reference = "hello"
[src/main.rs:9] reference = "world"

This basically does what my original example did, but it works. All I had to do was to change fn do_something(&self) to fn do_something(&'a self).

However. this fix does not work for drop, because of the following error:

   Compiling playground v0.0.1 (/playground)
error[E0308]: method not compatible with trait
  --> src/main.rs:25:5
   |
25 |     fn drop(&'a mut self) {
   |     ^^^^^^^^^^^^^^^^^^^^^ lifetime mismatch
   |
   = note: expected type `fn(&mut MyStruct<'a, Collection>)`
              found type `fn(&'a mut MyStruct<'a, Collection>)`
note: the anonymous lifetime #1 defined on the method body at 25:5...
  --> src/main.rs:25:5
   |
25 | /     fn drop(&'a mut self) {
26 | |         for reference in &self.references {}
27 | |     }
   | |_____^
note: ...does not necessarily outlive the lifetime 'a as defined on the impl at 20:6
  --> src/main.rs:20:6
   |
20 | impl<'a, Collection> Drop for MyStruct<'a, Collection>
   |      ^^

error: aborting due to previous error

For more information about this error, try `rustc --explain E0308`.
error: could not compile `playground`.

To learn more, run the command again with --verbose.

That makes me wonder, is it okay to use transmute to change &mut self to &'a mut self, in this case? If it is, that should solve my problem.

You can't change existing traits, so the very unusual requirement of &'a self is going to make it awful to work with. Lifetime on self is a huge red flag and it generally doesn't lead to anything good.

In practice in drop you may get away with that if you're careful not to do anything silly with it. But it's not safe to transmute lifetimes in general. In case of &mut it's not even correct to transmute longer-lived lifetime to a shorter lived, because it would allow you to assign something shorter lived to it, and then longer-lived context could cause a use-after-free error.

1 Like

You're right about that. I tried implementing the real drop-method in a regular impl with my proposed solution and encountered other lifetime issues. The correct way to implement it, would be akin to what you already mentioned previously, with the help of for<'a> &'a .... I filed a bug report for Rust with a version of the example, that should work, but doesn't and it seems, that this exact issue had already been found in 2016, which is bad news. However, the good news is, only a few hours before I reported the bug, someone made a pull request to fix this issue. Therefore, I'm fairly optimistic about the time it takes to solve the bug.

For reference:

1 Like

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.