Making a macro implementing a trait which needs to access a field of a struct

pub struct Variation {
  vars_count_on_reverse: i32,
  vars_count_on_forward: i32,
}

pub struct Sclip {
  vm: Variation,
  n: i32,
}

pub trait VariationTrait {
    fn inc_dir(&mut self, dir: bool);
}

#[macro_export]
macro_rules! impl_variation_trait {
    ($target_name:ident) => {
        impl_variation_trait!($target_name, self)
    };

    ($target_name:ident, $to_variation:ident) => {
        
        impl VariationTrait for $target_name {
            /**
             * Increment count for direction
             * @param dir false for forward strand, true for reverse strand
             */
            fn inc_dir(&mut self, dir: bool) {
                if dir {
                    $to_variation.vars_count_on_reverse += 1;
                } else {
                    $to_variation.vars_count_on_forward += 1;
                }
            }
}
}
}

// want to do the followings:
impl_variation_trait!(Variation)
impl_variation_trait!(Sclip, self.vm)

Hi. I try to make a macro, which implements a trait.
Some methods need to access the field (Variation) of Sclip.

The above code is what I've done, but it does not compile.

error: macros that expand to items must be delimited with braces or followed by a semicolon
   --> src/variations/Variation.rs:91:30
    |
91  |         impl_variation_trait!($target_name, self)
    |                              ^^^^^^^^^^^^^^^^^^^^
...
163 | impl_variation_trait!(Variation);
    | -------------------------------- in this macro invocation
    |
    = note: this error originates in the macro `impl_variation_trait` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0424]: expected value, found module `self`
   --> src/variations/Variation.rs:103:21
    |
101 | /             fn inc_dir(&mut self, dir: bool) {
102 | |                 if dir {
103 | |                     $to_variation.vars_count_on_reverse += 1;
    | |                     ^^^^^^^^^^^^^ `self` value is a keyword only available in methods with a `self` parameter
104 | |                 } else {
105 | |                     $to_variation.vars_count_on_forward += 1;
106 | |                 }
107 | |             }
    | |_____________- this function has a `self` parameter, but a macro invocation can only access identifiers it receives from parameters
...
163 |   impl_variation_trait!(Variation);
    |   -------------------------------- in this macro invocation
    |
    = note: this error originates in the macro `impl_variation_trait` (in Nightly builds, run with -Z macro-backtrace for more info)


Could you help me to fix this?
Any help would be appreciated.

pub struct Variation {
    vars_count_on_reverse: i32,
    vars_count_on_forward: i32,
}

pub struct Sclip {
    vm: Variation,
    n: i32,
}

pub trait VariationTrait {
    fn inc_dir(&mut self, dir: bool);
}

#[macro_export]
macro_rules! impl_variation_trait {
    ($target_name:ident) => {
        impl_variation_trait!($target_name, |self| self);
    };

    ($target_name:ident, |$self:ident| $to_variation:expr) => {
        
        impl VariationTrait for $target_name {
            /**
             * Increment count for direction
             * @param dir false for forward strand, true for reverse strand
             */
            fn inc_dir(&mut $self, dir: bool) {
                if dir {
                    $to_variation.vars_count_on_reverse += 1;
                } else {
                    $to_variation.vars_count_on_forward += 1;
                }
            }
        }
    };
}

// want to do the followings:
impl_variation_trait!(Variation);
impl_variation_trait!(Sclip, |self| self.vm);

Relevant fixes:

  • the macro invocations for expanding to an impl must be ;-terminated (or alternatively, use {} braces as in “impl_variation_trait!{…}”)
  • the self.vm part is more than just an ident, but a whole expression (expr) instead
  • the self keyword/identifier is affected by macro hygiene, so the macro caller needs to provide this keyword both for where it’s introduced (in the method signature) and where it’s used (in the field expression). I’m using a closure-inspired syntax, but that’s merely because I think it looks nice, and this has nothing to do with closures.
3 Likes

Thank you for answer.

Could you tell me the name of |$self:ident| in the macro code?

I can't find it in the rust book and reference page.

As explained above, that’s just my personal preference of how to structure this macro syntax. It has no meaning here and separating it by a comma (or various alternatives) would work just as well. I liked the syntax because it resembles the syntax of closures.

2 Likes

Note that macros can match almost arbitrary syntax, so anything outside the pre-defined metacharacters and meta-expressions/matchers ($matcher:kind, $(repetition)+, etc.) that is contained in a macro pattern will just be matched literally and has no intrinsic meaning.

1 Like