Advanced Lifetime Issues in Generic Trait

I have the following two traits:

pub trait ASTVisitor<T> {
    fn visit_module(&mut self, tlu: &Module) -> T;
    fn visit_function(&mut self, func: &Function) -> T;
    // more functions that are similar ...
}

pub trait ASTNode {
    fn visit<T>(&self, visitor: &mut impl ASTVisitor<T>) -> T;
}

The ASTVisitor trait is implemented by any struct that wants to traverse the AST. The ASTNode trait is implemented by any node in the AST that can be visited.

The following is an example implementation of ASTNode for Module:

#[derive(Debug, Clone, PartialEq, Default)]
pub struct Module { /* ... */}

impl ASTNode for Module {
    fn visit<T>(&self, visitor: &mut impl ASTVisitor<T>) -> T {
        visitor.visit_module(self)
    }
} 

Now, to my current challenge: I want to build an ASTVisitor, that keeps a reference to an ASTNode:

#[derive(Debug, Clone)]
pub struct Sema {
    current_func: Option<&Func>, // <-- !! Error: Expected named lifetime parameter !!
}

impl ASTVisitor<()> for Sema {
    fn visit_module(&mut self, tlu: &Module) {
        todo!()
    }

    /* ... */
}

The reference to Func needs a named lifetime. So far, so good. Everything makes sense. However, I couldn't figure out how to fix this. I've tried the following:

Playground Link to my attempted solution

Rust complains that *self was previously mutable borrowed, which is an odd error.

&'a mut Type<'a> is a well-recognized anti-pattern. Due to the invariance of mutable references in their referent type, &'a mut Type<'a> means that the value of type Type<'a> will be borrowed for the rest of its existence, making it completely unusable except through the mutable reference.

Blindly sprinkling the same lifetime everywhere won't get you anywhere. You have to understand which lifetime has to outlive which one. If your Sema needs to store Functions, then the Functions need to outlive the Sema and not the converse.

Consequently, ASTVisitor's methods shouldn't have the 'a lifetime parameter on self, and ASTNode's methods shouldn't have the 'a lifetime parameter on the visitor.

This compiles.

6 Likes

Excellent explanation and solution! Thank you!

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.