I need a HashMap which uses a NodeType enum as key and as values a Visitor struct with enter and exit fields of a type like Option<Box<Fn(Node, Option<Node>)>> (e.g. enter and exit are “optional” - they can be a function which accept a node and optionally a parent node). The Box is there because Fn will be a closure (similar to this example I guess).

However I struggle with implementing this. I already have something like NodeType called Node which is an enum which has associated data. I’d like to reuse this, but I don’t know how to use a certain type of Node as a key in the HashMap without worrying about the associated data. I guess it is not possible, but I’m not sure what is this best way to solve this.

But this doesn’t work, because found value NodeType::Programm used as a type for nodeType: NodeType::Programm.

Since there are limited types, it might be nicer to use a Vec instead of a HashMap.

Do you mean saving all visitors in a Vec and filter for the visitor I need?

Taking a step way back, why not just have a trait Visitor and implement this for Node?

I’m not sure if I need more than one visitor for a given node type in the future. I think the idea behind the given compiler is to run the traverse and transform step with different visitors on the “same” ast, so you could write plugins and custom transform steps.

When I have a trait Visitor and implement this for Node I can only have one visitor for a node type, right?

So Node is a struct here, right? And type a method returning the associated NodeType?

It doesn’t have to be a struct. You can implement a type method on the enum you had originally.

donaldpipowitch:

But this doesn’t work, because found value NodeType::Programm used as a type for nodeType: NodeType::Programm.

Right, each variant is not a type in itself. But a Node::Programm is always a NodeType::Programm, so you don’t need a field for this anyway. That’s what I was trying to show in the match node {...} above, that you can just match each Node variant to convert to the corresponding NodeType.

donaldpipowitch:

Since there are limited types, it might be nicer to use a Vec instead of a HashMap.

Do you mean saving all visitors in a Vec and filter for the visitor I need?

Not necessarily filter - you can set up the node type to represent the integral Vec index, just the same as it was the index of the HashMap.

donaldpipowitch:

When I have a trait Visitor and implement this for Node I can only have one visitor for a node type, right?

Hmm, right, it needs to be structured for the kind of visitor. There’s actually a demo visitor pattern here:

# Visitor
## Description
A visitor encapsulates an algorithm that operates over a heterogeneous
collection of objects. It allows multiple different algorithms to be written
over the same data without having to modify the data (or their primary
behaviour).
Furthermore, the visitor pattern allows separating the traversal of
a collection of objects from the operations performed on each object.
## Example
```rust
// The data we will visit
mod ast {
pub enum Stmt {
Expr(Expr),

It doesn’t have to be a struct. You can implement a type method on the enum you had originally.

Nice. Haven’t thought of that.

cuviper:

Right, each variant is not a type in itself. But a Node::Programm is always a NodeType::Programm, so you don’t need a field for this anyway. That’s what I was trying to show in the match node {…} above, that you can just match each Node variant to convert to the corresponding NodeType.

Now I have two other problems (one related to this issue, the other one more general):

1. no field body on type Node (see here) → I can now get the NodeType of a Node with node.get_type(), but how can I do it the other way around? I knownode is a Node::Programm here. How can I force Rust into knowing that? I can’t do something like (node as Node::Programm).body, because Node::Programm is a value and not a type.

This works, but it is very verbose. Can I simplify that, because I know the value of Node and maybe don’t need the match?

2. traverse_node unresolved name (see here) → It looks like a closure can call another closure only if it was declared before in Rust. What is the recommended pattern in Rust to solve this? Both closures need to call each other, so I can’t just switch the order.

This works, but it is very verbose. Can I simplify that, because I know the value of Node and maybe don’t need the match?

I think in this case, the outer match on NodeType is unnecessary, not really telling you anything you couldn’t get from Node directly. Just use the inner match on the node itself.

donaldpipowitch:

traverse_node unresolved name (see here) → It looks like a closure can call another closure only if it was declared before in Rust. What is the recommended pattern in Rust to solve this? Both closures need to call each other, so I can’t just switch the order.

That’s mutual recursion, and recursive closures in Rust are kind of hard.

Plain old functions might be ok for you, passing captured locals as parameters instead. You can still nest the functions in the same place you currently have the closures for scoping their visibility.

Or similarly, you can “desugar” the closures yourself. Create a local struct type with whatever captured data you need like the map, implement traverse_node and traverse_nodes as methods on that struct, and recurse to your heart’s content.