How to check types within macro

I want to create a macro that prints all its arguments accept when it finds an i32, it prints "Integer"

macro_rules! f{
    ($($arg:expr),*) => {
        $(println!("{}", $arg);)*
    };
}

fn main() {
    f!("hello", "world".to_string(), 43265);
}

macros can't specialize on types, in fact you can't specialize on types at all until we get specialization.

You can play around with specialization in nightly

1 Like

You can use specialization like so,

https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=bd2683227b5535201f13e267ee6a8756

Note: the straight-forward approach doesn't work yet because specialization isn't complete yet,

https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=ba348c8f3d3e2a4b666bcd75c8495f46

You may be able to do something like I did in this file starting at this line: https://github.com/Axelderan/componentile/blob/3cdf30215c9be1cc7541ac906e6635e4d8f4975a/src/lib.rs#L149

Note that this is only if you define the type as macro input text. So if you define the structure that type belongs in inside a macro, giving that macro just the literal string denoting that type name, you can do it.

Also you might be able to accomplish what you want here using the Any type instead and getting the typename from there. Look for the docs on that.

Contrary to the other answers, here is an implementation using 100% safe and stable code that does what you asked.

macro_rules! f {
    ($($arg:expr),*) => {{
        trait PrintInteger {
            fn as_printed(&self) -> &'static str {
                "Integer"
            }
        }
        impl PrintInteger for i32 {}
        trait PrintOther {
            fn as_printed(&self) -> &Self {
                self
            }
        }
        impl<T: std::fmt::Display> PrintOther for &T {}
        $(
            println!("{}", (&$arg).as_printed());
        )*
    }};
}

fn main() {
    f!("hello", "world".to_string(), 43265);
}
hello
world
Integer
8 Likes

So those impls expand within some extra curly braces you added to that macro. Are they scoped locally to just scope block? If I call that macro more than once, are there any issues?

This is cool.

I'm curious, why Rust doesn't complain about not being able to choose the method? How is it resolved it such case?

1 Like
  • <i32 as PrintInteger>::as_printed takes a & i32 as input,

  • <&'_ i32 as PrintOther>::as_printed takes a & &i32 as input,

So, not only does the method resolution not collide (each method / function is expecting different inputs), but also there is a clear order regarding which method shadows which. In this case, since &43265: &{integer} => &i32, the one not needing an auto-ref on its receiver wins.

4 Likes

Because of the scoping you can call it as many times as you like, it also "seals" the trait and implementations so they can only be used inside the macro.
playground

1 Like