"Visitor" for types?


#1

Assume I have:

trait X{
    fn x();
}

I now have implemented X for a number of structs, e.g. A, B, and C.
Ideally, I would love to be able to derive a method of the form

fn all_x()
{
    A::x();
    B::x();
    C::x();
}

Is that possible in some way? I have found any::TypeID, but I did not find a way of resolving TypeIDs back to types.

Edit: Eurgh, I managed to accidentally submit before my post was complete.
Edit 2: Formatting.


#2

No. Rust doesn’t have runtime type information, and the type information it does have at compile time can’t be accessed in any way from your code.

More edits: OK, OK… I mean, you can introspect types, but that requires a lint, which is a compiler plugin, which means the interface is effectively unsupported and changes on a regular basis. Also, lints can’t generate code, so you’d have to pull some TeX shenanigans to use that information.

What you could do is write a custom derive that you add to each type that implements X, and write those out to a file, which you then re-import on the next compile to generate all_x… but again, yuck.

Or, if you’re super desperate, you could use syn in a build script to parse your source code, find all the implementations of X (keeping in mind that you don’t have access to name or type resolution), and generate all_x that way.

But, honestly, I’d just write all_x by hand.


#3

Hmm. I think this should be possible with a macro, though, right?


#4

No. Macros are purely syntactic. As far as they’re concerned, types don’t exist.


#5

Hmm. It would be fine to repeat the list of types I want to be visited in this way, though.

I actually need a number of different methods that’ll visit all the relevant types in this way, and what I want to achieve is not to have to repeat the same list over and over again.


#6

Well, that depends on what exactly you’re trying to do. The most general approach would be something like:

macro_rules! x_impls {
    ($cb:ident!{ $($cb_args:tt)* }) => {
        $cb!{
            $($cb_args)*
            A, B, C
        }
    };
}

macro_rules! invoke_method {
    ($method:ident; $($ty_name:ident),*) => {
        fn all_x() {
            $(
                $ty_name::$method();
            )*
        }
    };
}

x_impls! { invoke_method! { x; } }

That way, you can pass the list to any number of macros.


#7

Thank you very much for your quick and competent help! I like this idea. If I may, I’ll use your code as building blocks for a bigger macro that’ll generate all the necessary methods.