Having trouble getting complex lifetimes system to compile

I’m working on a crate which uses lifetimes to verify the correctness of code that would otherwise be prone to bugs. This involves the use of unsafe cells and lifetime phantom data. I’m down to one compile error, but I’m very stuck on it.

I’ve created a MCVE that achieves the same compile error in the same way. https://play.rust-lang.org/?version=stable&mode=debug&edition=2015&gist=e161836aad3bf672a473b5c84a1e90db

use std::marker::PhantomData;

// owned
#[derive(Debug)]
struct Foo<'a> {
    array: &'a [i32; 2],
    index: usize,
}
impl<'a> Foo<'a> {
    fn to_bar<'x, 'y>(&'y mut self) -> Bar<'x, 'y>
        where 'a: 'x, 'a: 'y {
        Bar {
            array: self.array,
            index: self.index,
            phantom: PhantomData,
        }
    }
}

#[derive(Debug)]
struct Bar<'a, 'b: 'a> {
    array: &'a [i32; 2],
    index: usize,
    phantom: PhantomData<&'b mut ()>,
}
impl<'a, 'b: 'a> Bar<'a, 'b> {
    fn to_foo(&mut self) -> Foo<'a> {
        Foo {
            array: self.array,
            index: self.index
        }
    }
}

fn baz<'a>(mut foo: Foo<'a>) -> Foo<'a> {
    let foo_2 = {
        let mut bar = foo.to_bar();
        bar.to_foo()
    };
    if foo.index == 0 {
        foo
    } else {
        foo_2
    }
}

fn main() {}

I apologize that it is hard to tell what I am trying to achieve in this MCVE.

  • I have a type Foo, which represents a view into a data structure, &[i32; 2]. Multiple Foo can view the same data structure simultaneously.
  • I have a type Bar, which represents a view into a Foo. There must only be one Bar for a given Foo at any moment. For this reason, I have introduced a PhantomData<&'b mut ()>.
  • A Bar can produce a child Foo, which is not dependent on the Bar, only the parent Foo of the Bar. In the real code, this is used to traverse a tree of Foo.
  • I have a function baz which accepts a Foo and returns either that same Foo or a child Foo through the traversal system mentioned in the previous bullet point.

This is throwing a compile error:

error[E0597]: `foo` does not live long enough
  --> src/main.rs:38:23
   |
38 |         let mut bar = foo.to_bar();
   |                       ^^^ borrowed value does not live long enough
...
46 | }
   | - borrowed value only lives until here
   |
note: borrowed value must be valid for the lifetime 'a as defined on the function body at 36:8...
  --> src/main.rs:36:8
   |
36 | fn baz<'a>(mut foo: Foo<'a>) -> Foo<'a> {
   |        ^^

Honestly, I cannot figure out what the error means. I would appreciate help.

You’re probably looking for something like this:

use std::marker::PhantomData;

// owned
#[derive(Debug)]
struct Foo<'a> {
    array: &'a [i32; 2],
    index: usize,
}
impl<'a> Foo<'a> {
    fn to_bar<'y>(&'y mut self) -> Bar<'a, 'y> {
        Bar {
            array: self.array,
            index: self.index,
            phantom: PhantomData,
        }
    }
}

#[derive(Debug)]
struct Bar<'a, 'b> {
    array: &'a [i32; 2],
    index: usize,
    phantom: PhantomData<&'b mut ()>,
}
impl<'a, 'b> Bar<'a, 'b> {
    fn to_foo(&mut self) -> Foo<'a> {
        Foo {
            array: self.array,
            index: self.index,
        }
    }
}

fn baz<'a>(mut foo: Foo<'a>) -> Foo<'a> {
    let foo_2 = {
        let mut bar = foo.to_bar();
        bar.to_foo()
    };
    if foo.index == 0 {
        foo
    } else {
        foo_2
    }
}

fn main() {}

The issue is baz says it returns a Foo<'a>, with the 'a being chosen by the caller (and associated with the foo parameter), but foo.to_bar() returns a shorter lived Bar than 'a, and you can’t get the original 'a back from it.

I’m on mobile so will stop here but feel free to ask follow-up and I’ll get back to you (or someone else will).

3 Likes

Thank you so much! I've been struggling with this all day. Not only does my code compile, it's way cleaner.

1 Like