How to check if two trait objects are of the same type?

#1

I have a trait T and many implementing structs S_1, ..., S_n.

Given two Box<dyn T>, how can I figure out if both trait objects are of the same type? I’m not interested in what the actual types are, just if they are the same or not.

My current approach is to add a function fn id(&self) -> i32 to T that returns a different constant for each type. But this is labourous and error-prone.

Is there a better way to do this?

1 Like
#2

If your structs don’t have any reference you can do:

box1.type_id() == box2.type_id()

If they do I’m afraid you’ll have to keep your method.

3 Likes
#3

You could have T inherit from Any and then check if their .type_id() are equal.

3 Likes
#4

Thank you very much people, the type_id approach works!

#5

For what it’s worth, needing this operation is often a sign that the S types might make a good enum. This has other advantages, like letting you avoid heap allocation if desired.

11 Likes
#6

Actually I thought about the same a few times already. This might actually be the way to go here, assuming matching enums is as fast as a vtable access.

#7

In the projects I’ve been working on recently, matching enums is often significantly faster than dynamic dispatch (on Intel machines).

The main reason you wouldn’t do this is if the set of options needs to be extensible by clients. Traits can be implemented by people other than you, but only you can extend this enum.

6 Likes
#8

Thank you for this elaboration. From your explanation using an enum is the much better choice for my crate. I will refactor that. Thank you very much!

#9

If you’re in a situation where you have a trait object, you can only supply that object to functions that work with that trait. It’s sort of a lowest-common-denominator polymorphism. So why do you want to know the type? I’m just curious what your use case is here.

#10

Hey ajnye,

I’m writing a parser for arithmetic and boolean expressions that emerges more and more into an interpreted programming language. I parse an expression into an operator tree, with infix operators that can have more than two arguments. The operator in question is a , for constructing tuples.
When constructing the operator tree, consecutive commas should be merged into a single node with a child for each argument, instead of each being a node with exactly two children. This is to allow the parser to differentiate between nested and non-nested tuples.
I represent operators in my operator tree as Box<dyn Operator> , so I needed a way to figure out if two nodes have operators of the same type to decide if they should be merged into one.

But as suggested by cbiffle, I will switch from the polymorphic approach to an enum approach. That will keep the operator types accessible and makes features that require context sensitive tree insertion with more context than just the precedence hopefully simpler to implement.

2 Likes
#11

It is definitely more typical to represent ASTs as enums than trait objects.

4 Likes