#Recursive Lifetimes and Higher Ranked Lifetimes

5 messages · Page 1 of 1 (latest)

next barn
#

I'm trying to find the correct syntax to solve a problem.
The goal is to create a trait that allows visiting it's type in a nested fashion. Ie if you have a large tree structure, where each tree item might be in different vecs on the same type, or even non vec data structures, and then you visit each node in the tree and call a passed in closure on the node.
And the point of implementing the trait on a reference type wrapper, is so you could create a reference enum wrapper, and make this whole thing work on multiple different types.
Was trying to avoid dyn because of performance, but starting to think there is no way around it. As my code (which is the closest Ive gotten to solving it):

pub trait MyTrait: Sized{
    type MyStruct<'a> : MyTrait<MyStruct<'a> = Self::MyStruct<'a>>;
    fn visit_child_mut<F>(&mut self, index: usize, visitor: F)
    where F: for<'b, 'c> FnMut(&'b mut Self::MyStruct<'c>);
}

pub struct VariantMut<'a>{
    ref_data: &'a mut Recursive,
}

pub struct Recursive{
    data: Vec<Recursive>
}

impl<'a> MyTrait for VariantMut<'a>{
    type MyStruct<'b> = VariantMut<'b>;
    fn visit_child_mut<F>(&mut self, index: usize, mut visitor: F) where
    F: for<'b, 'c> FnMut(&'b mut VariantMut<'c>), {
        let mut child_data = VariantMut{
            ref_data: &mut self.ref_data.data[index]
        };
        visitor(&mut child_data)
    }
}

fn render_children<T: for<'a>MyTrait<MyStruct<'a> = T>>(
    node: &mut T,
    render_fn: &impl Fn(&mut T),
) where
{
    for index in 0..3 {
        node.visit_child_mut(index, |child| {
            render_fn(child);
            render_children(child, render_fn);
        });
    }
}

fn main() {
    let mut root = Recursive {
        data: vec![
            Recursive { data: vec![Recursive { data: vec![] }, Recursive { data: vec![] }, Recursive { data: vec![] }] },
            Recursive { data: vec![Recursive { data: vec![] }, Recursive { data: vec![] }, Recursive { data: vec![] }] },
            Recursive { data: vec![Recursive { data: vec![] }, Recursive { data: vec![] }, Recursive { data: vec![] }] },
        ],
    };
    let mut root_variant = VariantMut {
        ref_data: &mut root,
    };
    let render_fn = |node: &mut VariantMut| {
        println!("Rendering node with {} children", node.ref_data.data.len());
    };
    render_children(&mut root_variant, &render_fn);
}

Results in an error:

error[E0597]: `root` does not live long enough
  --> src/main.rs:51:19
   |
41 |     let mut root = Recursive {
   |         -------- binding `root` declared here
...
51 |         ref_data: &mut root,
   |                   ^^^^^^^^^ borrowed value does not live long enough
...
60 |     render_children(&mut root_variant, &render_fn);
   |     ---------------------------------------------- argument requires that `root` is borrowed for `'static`
61 | }
   | - `root` dropped here while still borrowed
   |
note: due to current limitations in the borrow checker, this implies a `'static` lifetime
  --> src/main.rs:29:28
   |
29 | ) where T: for<'a> MyTrait<MyStruct<'a> = T>
   |                            ^^^^^^^^^^^^^^^^

error[E0308]: mismatched types
  --> src/main.rs:60:5
   |
60 |     render_children(&mut root_variant, &render_fn);
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other
   |
   = note: expected struct `VariantMut<'a>`
              found struct `VariantMut<'_>`
note: the lifetime requirement is introduced here
  --> src/main.rs:29:28
   |
29 | ) where T: for<'a> MyTrait<MyStruct<'a> = T>
   |                            ^^^^^^^^^^^^^^^^

due to current limitations in the borrow checker, this implies a 'static lifetime
Does this mean its not possible to solve this?

#

I have gotten this to work if the render_ children function was type specific, but I am trying to make it generic over the type (that's the whole point of the trait)

stuck matrix
# next barn I'm trying to find the correct syntax to solve a problem. The goal is to create ...

What you're expressing with T: for<'a> MyTrait<MyStryct<'a> = T> cannot work for T = VariantMut<'_>.
That would imply that given some lifetime 'root like the one you have in main, then for<'a> VariantMut<'root>: MyTrait<MyStruct<'a> = VariantMut<'root>> would need to hold
This is obviously not the case, since you'd need ... = VariantMut<'a> instead of ... = VariantMut<'root>
The only type T that would work here is VariantMut<'static>, since it's a subtype of VariantMut<'lt> for any 'lt
That's why the compiler is asking for a VariantMut<'static>.
The limitation here is rather that you can't really express what you want, which would be something like

T: <'_> + for<'a> MyTrait<MyStruct<'a> = T<'a>>
```where `T: <'_>` would mean that `T` is *higher kinded* over a lifetime, and in your case it would be the `VariantMut` *type constructor*, which given a lifetime `'lt`, produces the *type* `VariantMut<'lt>`.
This is not valid syntax, but there are some ways to express something close to it, using an intermediary trait
#

If you want to learn more, and potentially commit to this pattern, it can be rendered more elegant by using the higher-kinded-types crate