Accepting a closure in a method of an object-safe trait?

I’m using a trait for an abstract syntax tree (alas, an enum is not an option because I want plugins to be able to add node types). So far, everything has worked well, with one exception:

pub trait SyntaxNode {
    fn traverse_mut<F>(&mut self, f: F)
    where
        F: FnMut(&mut dyn SyntaxNode);
}

Problem: Due to generics, SyntaxNode isn’t object-safe anymore.

I initially used an fn type but then I can’t use closures and collect data via side effects. What is the most elegant solution or workaround in this case? Thanks!

1 Like

Use

fn traverse_mut<F>(&mut self, f: &mut dyn FnMut(&mut dyn SyntaxNode));

if you need object-safety.

If you want more convenience for the caller, you can create a convenience-wrapper with a Self: Sized bound, too.

fn traverse_mut_dyn(&mut self, f: &mut dyn FnMut(&mut dyn SyntaxNode));

fn traverse_mut<F>(&mut self, f: F)
where
    Self: Sized,
    F: FnMut(&mut dyn SyntaxNode),
{
    self.traverse_mut_dyn(&mut { f })
}

In cases where you already have a &mut dyn FnMut(&mut dyn SyntaxNode) at hand, make sure to still call traverse_mut_dyn directly, since calling traverse_mut instead would add another layer of dynamic function calls and indirections; would be particularly bad in a (set of) recursively implemented trait impl(s) in the definition(s) of other traverse_mut_dyn methods.

4 Likes

Perfect, thanks!

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.