Simple Macro Example Help

Hi Folks,

I am coming from the C/C++ languages and I know the macro system in rust is very powerful because it gives alot to programmers in terms of of things like fancy tokenizing and of course the decorative style of macros.

I am trying to do something, simple and lot of example focus on the more advanced features. I am doing something very simple, and haven found examples to help out on just basic macros; lets not focus on if this macro can be done in none macro way, I am just trying this out to get a little bit familiar with how macros are done:

macro_rules! TEST {
( $a:ident, $b:ident, $v:expr ) => {
self.reg_map.insert(CPURegs::$a, ($v & 0xFF00) >> 8);
self.reg_map.insert(CPURegs::$b ,$v & 0x00FF);
};
}

So here CPURegs is an enum, and I have a few places where I do this with different enum values, so I thought why not write a macro just to generate some code do these inserts, by taken he macro value name and the value to insert into a struct member hashmap. Where I am using the macro self, should be in scope:

fn set_reg(&mut self, dest: CPURegs, val: u16) {
TEST!(H,L, val);
}

And I get: ^^^^ self value is a keyword only available in methods with a self parameter , as the error. Also not sure of ident and expr are the right token type here, but its what I I wanted to try with.

Thanks Again!

To enforce macro hygiene, you can't use self in a macro like that. You need to explicitly pass in self as an argument; something like

macro_rules! TEST {
    ($self:ident, $a:ident, $b:ident, $v:expr) => {
        $self.reg_map.insert(CPURegs::$a, ($v & 0xFF00) >> 8);
        $self.reg_map.insert(CPURegs::$b, $v & 0x00FF);
    };
}

fn set_reg(&mut self, dest: CPURegs, val: u16) {
    TEST!(self, H, L, val);
}
1 Like

you need to pass self into the macro.

macro_rules! TEST {
( $self:ident, $a:ident, $b:ident, $v:expr ) => {
$self.reg_map.insert(CPURegs::$a, ($v & 0xFF00) >> 8);
$self.reg_map.insert(CPURegs::$b ,$v & 0x00FF);
};
}

then call like

fn set_reg(&mut self, dest: CPURegs, val: u16) {
TEST!(self, H,L, val);
}

Or you need to declare the macro in set_reg

fn set_reg(&mut self, dest: CPURegs, val: u16) {
macro_rules! TEST {
( $a:ident, $b:ident, $v:expr ) => {
$self.reg_map.insert(CPURegs::$a, ($v & 0xFF00) >> 8);
$self.reg_map.insert(CPURegs::$b ,$v & 0x00FF);
};
}

TEST!(self, H,L, val);
}

so that self is in scope.

Otherwise self in the macro could refer to some other self. Note that identifiers do not uniquely identify a value:

let foo = 10_i32;
let foo = "hello";

macro_rules! foo_macro {
() => { foo }
}

// what's the type of `bar`?
let bar = foo_macro!();

It's &'static str because that's the foo being referenced. We can change this by moving where the macro is declared.

let foo = 10_i32;

macro_rules! foo_macro {
() => { foo }
}

let foo = "hello";

// what's the type of `bar`?
let bar = foo_macro!();

Now the type of bar is i32 because that's the foo being referenced. (This is conceptually similar to closure captures)

In a similar way, self in your macro could refer to anything depending on where it was declared.

1 Like

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.