Lifetimes and Borrowing Trouble

link to code - https://github.com/starduv/lil-schemy/blob/ea8ac2e8049f3b8bcf069374e7cd2e0c40c79508/native/src/open_api/generate_schema.rs#L54

diagnostic error

error[E0597]: `node` does not live long enough
  --> src/open_api/generate_schema.rs:54:18
   |
45 | fn find_paths<'m, 'n : 'm>(
   |               -- lifetime `'m` defined here
46 |     open_api: &mut OpenApi,
47 |     node: SchemyNode<'n>,
   |     ---- binding `node` declared here
...
54 |     for child in node.children() {
   |                  ^^^^^^^^^^^^^^^ borrowed value does not live long enough
...
70 |             _ => find_paths(open_api, child.clone(), file_path, deferred_schemas, symbol_tables),
   |                  ------------------------------------------------------------------------------- argument requires that `node` is borrowed for `'m`
...
73 | }
   | - `node` dropped here while still borrowed

The link to github shows different code than the snippet here. Which one is correct?

The permalink goes to line 54. The snippet is the diagnostic that also highlights line 54. Oh, in the diagnostic i see the additional lifetime. Either way, i get the same error and it doesn't work.

The root issue is likely the data structure

pub enum SchemyNode<'m> {
    BlockStmt {
        node: &'m BlockStmt,
        parent: Option<Box<&'m SchemyNode<'m>>>,
    },
    /* and more similar variants… */
}

Then your children function creates values with back-pointers to the parents, so it requires a &'m self borrow; but your use-site, the find_paths function, violates this requirements in that it owns the node argument, yet is expected to be able to return (indirectly, via the symbol_tables argument) references to it. To complete the issue, this applies not only to node, but via the recursive calls also to the children, so there’s very clearly newly generated SchemyNode values that nobody can own after the find_paths function terminates.


Back-pointers in Rust are uncommon in general, mostly we avoid them. If we don’t, references are commonly not the right tool for representing pointers in nested data structures, in particular if you aren’t very certain who else is going to own all those nodes. Migrating to some usage of Arc is a potential alternative candidate solution, if avoiding them isn’t an option. In any case, data structures without any lifetime parameters are infinitely easier to work with, and generally the recommended path, especially at a beginner level of Rust usage. (On that node, it’s a pity that the compiler so easily suggests you to add them, if you ever write a &T type into any field).

For any more concrete tips on the best approach for your code I’d need to dig deeper than the preliminary glance at the code that I’m doing at the moment.

i wanted to avoid cloning up a tree of nodes. now i see why i need Arc. thanks!