简体   繁体   中英

Access to self from the parameters of a macro that creates a struct

I'm trying to write a macro that generates a struct. The implementation for the struct will be generated by the macro, but some blocks of code will be provided as macro arguments. Here is a minimal example of such a macro:

macro_rules! make_struct {
    ($name:ident $block:block) => {
        struct $name {
            foo: i32,
        }

        impl $name {
            fn new() -> Self {
                $name { foo: 42 }
            }
            fn act (&self) {
                $block
            }
        }
    };
}

And here is an example of how the macro can be used:

fn main() {
    // Works
    make_struct!(Test { println! ("Bar: {:?}", 24); });
    let test = Test::new();
    test.act();
}

I would like to give access to self inside the supplied code. Something like:

fn main() {
    // Does not work
    make_struct!(Test { println! ("Foo: {:?}", self.foo); });
    let test = Test::new();
    test.act();
}

I understand that this does not work because of macro hygiene rules. Specifically, the self.foo expression is evaluated in the syntax context of the main function, where self does not exist. The question is: is there a way to modify the macro so that self may be accessed from the user-supplied code?

Code on playground

You can pass a closure instead of a block.

make_struct!(Test |this| println!("Foo: {:?}", this.foo));

Then the macro can use the closure and call it with self :

macro_rules! make_struct {
    ($name:ident $closure:expr) => {
        struct $name {
            foo: i32,
        }

        impl $name {
            fn new() -> Self {
                $name { foo: 42 }
            }
            fn act (&self) {
                let x: &Fn(&Self) = &$closure;
                x(self)
            }
        }
    };
}

The dance with the let binding is necessary, because the type of this in the closure can't be inferred (yet?). And it also makes your macro's error reporting a little more readable when something other than a closure is passed.

Found a way to do it by adding a parameter to the macro that stores the name by which self will be accessed in the blocks:

macro_rules! make_struct {
    ($myname:ident : $type_name:ident $block:block) => {
        struct $type_name {
            foo: i32,
        }

        impl $type_name {
            fn new() -> Self {
                $type_name { foo: 42 }
            }
            fn act (&self) {
                let $myname = self;
                $block
            }
        }
    };
}

fn main() {
    make_struct!(myself: Test { println! ("Foo: {:?}", myself.foo); });
    let test = Test::new();
    test.act();
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM