[Question]: Enum and method names

Hello.
I need to get the name of an enum::struct instance.

pub enum Node {
    IntNode   {value: i32},
    FloatNode {value: f32}
}

let e = Node::IntNode {value: 100};
let name = somefunctionname(e); // Something like `getStructName(e)`
name // Should return a String or &str with the value `IntNode`

Then I need to get a method by that string.

impl Interpreter {
    fn visit(&mut self, funcname: String) {
        let funcname = format!('visit_{}', funcname);
        let method = somefunctionname(funcname); // Something like `getMethod(funcname, self)`
        method(); // If funcname is `visit_IntNode` then this is equivalent to `self.visit_IntNode();`
    }

    fn visit_IntNode(&mut self) {}
}

If one of these is not possible, please let me know of a good alternative.

This sort of dynamic runtime reflection isn't possible in rust. Why do you want to do such a thing?

As you're always going to be choosing functions from a fixed set (as you cant add named functions at runtime), so why not use a enum to represent all posibilitys, and match on it to decide which function to call.

1 Like

I'm coming from Python where I could have just done this:

funcname = type(e).__name__
method = getattr(f'visit_{funcname}', self)
method()

I just realised that I could do:

if funcname == "IntNode" {self.visit_IntNode()}
elif ...

but that's just tedious so I don't really want to do that. Which is why I was hoping for a get enum struct name, then get a method by that String.

Well, tedious it is. It works it's not that tedious actually.

This looks like what you want. Stringly typed API's are almost always bad as a string is not very expressive for when you dont want to encode any possible text.

Thanks

Is there still a way to get the name of a struct (not in an enum)?

See std::any::type_name and std::any::type_name_of_val
But make sure you note the Note section of these methods. Most importantly,

This is intended for diagnostic use.... In addition, the output may change between versions of the compiler.

This getattr() pattern, while idiomatic in dynamic languages like Python and Ruby, is often frowned upon by statically typed languages*.

If you are recursively visiting nodes, pass a &Node to the visit() method and then use a match statement to run different code depending on the variant. That way you are guaranteed to handle every branch and if nodes get renamed or added over time you'll immediately get a compile error telling you to update your code.

pub enum Node {
    IntNode   {value: i32},
    FloatNode {value: f32}
}

impl Interpreter {
    fn visit(&mut self, node: &Node) {
       match node {
            Node::IntNode { value } => self.visit_int_node(value),
            ...
        }
    }

    fn visit_int_node(&mut self, value: i32) {}
}

* The reason this is frowned upon is because:

  • It tends to be quite error-prone - what if I accidentally called my method visit_ItnNode instead of visit_IntNode? What if the value is expecting a string, but I pass it an int?
  • The API is hard to discover (intellisense can't help you), and
  • it's easy to introduce bugs when code gets refactored, variants are added, or names are changed
4 Likes