Exclude equal tokens in a macro doing a cartesian product

I have a Convert trait that I want to implement for all numeric types. The trait in question also has a generic implementation for self-conversion I don't want to get rid of. The code I have correctly generates all the implementations for all types, but the cartesian product also includes identity conversions which are already specified by the generic implementation and I have to avoid that to get the code to compile.

Here's a isolated snippet of the code I have: Rust Playground

Is there a proc macro that stringifies two tokens and allows excluding some tokens if the token strings match?

macro_rules! inner {
    ($invoke: ident, [], [$(,)?$($b_rest: ty),*], [$($carry: ty),+]) => {};
    ($invoke: ident, [$a: ty $(,$a_rest: ty)*], [], [$($carry: ty),+]) => {
        inner!($invoke, [$($a_rest),*], [$($carry),+], [$($carry),+]);
    };
    ($invoke: ident, [$a: ty $(,$a_rest: ty)*], [$b: ty $(,$b_rest: ty)*], [$($carry: ty),+]) => {
        $invoke!($a, $b);
        inner!($invoke, [$a$(,$a_rest)*], [$($b_rest),*], [$($carry),+]);
    };
}

I basically want to exclude $invoke!($a, $b); part if string representations of $a and $b are equal.

It doesn't have to be a string comparison from a proc macro, but I think that's the simplest route as exclusion of tokens from a $($b_rest),* (i.e. getting A⊗A \ {(a, a): a∈A}) requires a proc macro as well, which is more complicated.

Both are not too hard to write, I'm just asking whether there's good already-established solutions or a way to use only declarative macros to achieve the same result.

P.S. I'm not using From and Into because Convert handles conversion of external types. I need the implementation for numbers to simplify conversion of vector types.

I know one way to do this, but it unfortunately requires an unstable feature, which makes it not really useful in practice. To be clear: do not use this. If you can't find another way, I'd make a procedural macro that does this and use that instead.

#![feature(macro_metavar_expr)]

macro_rules! if_tok_cmp {
    (
        ($($a:tt)*) == ($($b:tt)*)
        { $($then_tts:tt)* }
        else { $($else_tts:tt)* }
    ) => {
        {
            macro_rules! __if_tok_cmp_check {
                ($($a)*) => { $($then_tts)* };
                ($$($$tts:tt)*) => { $($else_tts)* };
            }
            __if_tok_cmp_check! { $($b)* }
        }
    };
}

macro_rules! conditional {
    ($tok:tt) => {
        if_tok_cmp! { ($tok) == (doggy) { "god street" } else { "mogu mogu" } }
    };
}

fn main() {
    let s1 = conditional!(doggy);
    let s2 = conditional!(kitty);
    println!("{s1:?}, {s2:?}");
}

That's a great RFC I didn't know of, thanks for pointing it out. Sadly the project doesn't use nightly, but I'm looking forward to using it some time in the future.

Found tt-call and tt-equal crates this morning which add exactly what I need (tt matching).

1 Like