0
votes

I'm trying to build the following macro that impls add-assign for various types by casting the type to an already impled type then carrying out the operation as expected:

macro_rules! assign_for_value {
    ( $type_to_assign:ty, $cast_to_type:ty, $Add_or_Sub_Assign:ty, $add_or_sub_assign:ty, $add_or_sub:tt ) => {
        impl $Add_or_Sub_Assign<$type_to_assign> for ValueTerm {
            fn $add_or_sub_assign(&mut self, rhs: $type_to_assign) {
                *self $add_or_sub= rhs as $cast_to_type;
            }
        }
    };
}

assign_for_value!(i8, i64, AddAssign, add_assign, +);
assign_for_value!(i8, i64, SubAssign, sub_assign, -);

I wanted to double it up as an assignment for all operation types although I'm getting the following error:

error: expected `::`, found keyword `for`:

impl $Add_or_Sub_Assign<$type_to_assign> for ValueTerm {
                                         ^^^ expected `::`

This error is bizarre and not really any help at explaining what I have done wrong. Can't seem to find anything similar to this on stackoverflow so thought I'd ask. Would be super grateful for any help on this:)

1
Rust macros don't concatenate text, they work on AST level. Once you've requested to parse SubAssign as a ty, you can't just stick a <T> to it to make it another kind of type. Similarly, $add_or_sub= will not work because += is a single token, not the concatenation of the + token with a = token, but you have forced the + to be its own token. - mcarton
ah ok I see, thanks @mcarton the info :) - Rob123

1 Answers

1
votes

You can make $Add_or_Sub_Assign and $add_or_sub_assign into identifiers (:ident). That way they can be used as part of a type and method name respectively (the latter was never a type to begin with). You will need to follow what was mentioned in the comments and make += a single token instead of trying to split it.

macro_rules! assign_for_value {
    ( $type_to_assign:ty, $cast_to_type:ty, $Add_or_Sub_Assign:ident, $add_or_sub_assign:ident, $add_or_sub:tt ) => {
        impl $Add_or_Sub_Assign<$type_to_assign> for ValueTerm {
            fn $add_or_sub_assign(&mut self, rhs: $type_to_assign) {
                *self $add_or_sub rhs as $cast_to_type;
            }
        }
    };
}

assign_for_value!(i8, i64, AddAssign, add_assign, +=);
assign_for_value!(i8, i64, SubAssign, sub_assign, -=);

See it running on the playground.