#macro_rules macro that checks if two types are the same and then emits a type

15 messages · Page 1 of 1 (latest)

haughty rampart
#
struct True;
struct False;

struct A;
struct B;

macro_rules! equals {
    // Match when both types are the same
    ($type1:ty, $type1:ty) => {
        True
    };
    // Match when types are different
    ($type1:ty, $type2:ty) => {
        False
    };
}

// Usage examples
type Result1 = equals!(A, A); // expands to True
type Result2 = equals!(A, B); // expands to False

this doesn't compile:

   Compiling playground v0.0.1 (/playground)
error: duplicate matcher binding
 --> src/lib.rs:9:26
  |
9 |     (@internal $type:ty, $type:ty) => {
  |                --------  ^^^^^^^^ duplicate binding
  |                |
  |                previous binding

error: could not compile `playground` (lib) due to 1 previous error

for context, I'd be using this macro internally inside another macro, which would operate on combinations of types, e.g. for A, B, C
generate something effectively like:

impl Foo for A {
    type Mask = (True, False, False)
}

impl Foo for B {
    type Mask = (False, True, False)
}

impl Foo for C {
    type Mask = (False, False, True)
}

I'm at the stage inside the type Mask = $( equals!($this, $that) ),+ but can't figure out how to implement that.

fleet dock
#

you could skip the equality if you generated the Trues and Falses when doing the combinations

haughty rampart
#

what I'm actually trying to achieve is:

#[derive(Debug)]
struct With<T>(T);
#[derive(Debug)]
struct Without<T>(T);
trait Disjoint {
    type Only;
}

#[derive(Debug)]
struct A;
#[derive(Debug)]
struct B;
#[derive(Debug)]
struct C;

// macro_rules! disjoint {
//     // your implementation here
// }

// calling disjoint!(A, B, C) should generate the following code:

// impl Disjoint for A {
//     type Only = (With<A>, Without<B>, Without<C>);
// }

// impl Disjoint for B {
//     type Only = (Without<A>, With<B>, Without<C>);
// }

// impl Disjoint for C {
//     type Only = (Without<A>, Without<B>, With<C>);
// }
#

what I currently have is this:

macro_rules! disjoint {
    // Entry point for the macro. Accepts a list of types.
    ($($t:ty),+) => {
        disjoint!(@for [$($t),*] ($($t),*));
    };

    (@for [$($t:ty),+] $x:tt) => {
        // For each type in the list, generate an implementation of Disjoint
        $(
            // Call the helper rule to generate the implementation
            disjoint!(@impl $t, $x);
        )*
    };

    // Helper rule for generating the implementation. This part handles creating
    // the correct tuple type for the `Only` associated type.
    (@impl $current:ty, ($($t:ty),+)) => {
        impl Disjoint for $current {
            type Only = (
                $(
                    disjoint!(@wrap $current, $t)
                ),+
            );
        }
    };
    
    // Helper rule to determine whether to use `With` or `Without`
    (@wrap $current:ty, $t:ty) => {
        disjoint!(@wrap_inner $current, $t)
    };
    (@wrap_inner $current:ty, $current:ty) => {
        With<$current>
    };
    (@wrap_inner $current:ty, $t:ty) => {
        Without<$t>
    };
}
#

it fails on the @wrap_inner

#

duplicate matcher binding

fleet dock
# haughty rampart what I currently have is this: ```rs macro_rules! disjoint { // Entry point ...

?play ```rs
macro_rules! disjoint {
// Entry point for the macro. Accepts a list of types.
($($t:ty),+) => {
disjoint!(@for [] [$($t)] ($($t)));
};

(@for [$($with:tt)*] [$curr:tt $($t:tt)*] $x:tt) => {
    // The number of Withouts is equal to however many types we already consumed
    disjoint!(@fill [] [$($with)*] $x $curr);
    // Add a Without to the list
    disjoint!(@for [Without $($with)*] [$($t)*] $x);
};

(@for $_:tt [] $__:tt) => {};

// Generate a list with `Without<$t>` at the beginning
(@fill [$($acc:tt)*] [$with:tt $($withs:tt)*] ($t:tt $($ts:tt)*) $curr:ty) => {
    disjoint!(@fill [ $($acc)* $with<$t> ] [$($withs)*] ($($ts)*) $curr);
};

// We meet at the place with the current type, add a `With<$t>`
(@fill [$($acc:tt)*] [] ($t:tt $($ts:tt)*) $curr:ty) => {
    disjoint!(@fill_without [ $($acc)* With<$t> ] ($($ts)*) $curr);
};

// The rest is `Without<$t>`
(@fill_without [$($acc:tt)*] ($t:tt $($ts:tt)*) $curr:ty) => {
    disjoint!(@fill_without [ $($acc)* Without<$t> ] ($($ts)*) $curr);
};

// Use the accumulated list to impl the type
(@fill_without [$($acc:tt)*] () $curr:ty) => {
    disjoint!(@impl [$($acc)*] $curr);
};

// Helper rule for generating the implementation. This part handles creating
// the correct tuple type for the `Only` associated type.
(@impl [$($t:ty)*] $current:ty) => {
    impl Disjoint for $current {
        type Only = (
            $($t),+
        );
    }
};

}

#[derive(Debug, Default)]
struct With<T>(T);
#[derive(Debug, Default)]
struct Without<T>(T);
trait Disjoint {
type Only;
}

#[derive(Debug, Default)]
struct A;
#[derive(Debug, Default)]
struct B;
#[derive(Debug, Default)]
struct C;

// trace_macros!(true);
disjoint!(A, B, C);

fn main() {
dbg!(<B as Disjoint>::Only::default());
}

desert nestBOT
#
[src/main.rs:66:5] <B as Disjoint>::Only::default() = (
    Without(
        A,
    ),
    With(
        B,
    ),
    Without(
        C,
    ),
)```
fleet dock
#

expansion is too large for the bot message lol

haughty rampart
#

?play

macro_rules! disjoint {
    // entry point: 2+ types
    ($current:ty, $( $rest:ty),*) => {
        disjoint!(@for [] $current [ $( $rest , )* ]);
    };

    // entry point: 1 type
    ($current:ty) => {
        disjoint!(@for [] $current []);
    };
    
    // 2+ remaining
    (@for [ $( $consumed:ty , )* ] $current:ty [ $next:ty, $( $later:ty , )* ]) => {
        disjoint!(@imp [ $( $consumed , )* ] $current [ $next , $( $later , )* ]);
        disjoint!(@for [ $( $consumed , )* $current , ] $next [ $( $later , )* ]);
    };
    
    // 1 remaining
    (@for [ $( $consumed:ty , )* ] $current:ty [ $next:ty ]) => {
        disjoint!(@imp [ $( $consumed , )* ] $current [ $next ]);
        disjoint!(@for [ $( $consumed , )* $current , ] $next []);
    };
    
    // 0 remaining
    (@for [ $( $consumed:ty , )* ] $current:ty []) => {
        disjoint!(@imp [ $( $consumed , )* ] $current []);
    };

    (@imp [ $( $before:ty , )* ] $current:ty [ $( $after:ty , )* ]) => {
        impl Disjoint for $current {
            type Only = (
                $(Without<$before> , )*
                With<$current> ,
                $(Without<$after> , )*
            );
        }
    };
}

#[derive(Debug, Default)]
struct With<T>(T);
#[derive(Debug, Default)]
struct Without<T>(T);
trait Disjoint {
    type Only;
}

#[derive(Debug, Default)]
struct A;
#[derive(Debug, Default)]
struct B;
#[derive(Debug, Default)]
struct C;
#[derive(Debug, Default)]
struct D;

// trace_macros!(true);
disjoint!(A, B, C, D);

fn main() {
    dbg!(<A as Disjoint>::Only::default());
}
desert nestBOT
#
[src/main.rs:61:5] <A as Disjoint>::Only::default() = (
    With(
        A,
    ),
    Without(
        B,
    ),
    Without(
        C,
    ),
    Without(
        D,
    ),
)```
haughty rampart
#

Thanks so much for this. I found it a bit hard to follow all the recursion so I thought I'd rewrite it using the additional tools you gave me: Using trace_macros!, inverting the impl and the wrapping, accumulating into before and after lists.

#

(Then I spent some time struggling with the commas)

#

I've learnt a lot.