Macro that matches arbitrary field accesses?

I have the following macro:

macro_rules! project {
    (&($c:expr).$($f:tt).*) => {{
        let c = $c;
        unsafe { $crate::project(c, |inner| ::core::ptr::addr_of!((*inner).$($f).*)) }
    }};
}

This successfully matches field accesses like:

project!(&(a).b.c);
project!(&(get_a()).b.c);

However, it doesn't work for index operations, which I was hoping the $f:tt would match:

project!(&(a).b.[0]);
project!(&(a).b.[1..2]);

Is there any way to get this to match both normal field accesses and indexing operations? Better yet, is there a way to do that without using the leading period (.[0]) syntax?

1 Like

You can blindly copy them all as tt instead.

macro_rules! project {
    (&($c:expr) $($f:tt)*) => {{
        let c = $c;
        unsafe { $crate::project(c, |inner| ::core::ptr::addr_of!((*inner) $($f)*)) }
    }};
}
1 Like

It does match index operations. Here's a modified version:

macro_rules! project {
    (&($c:expr).$($f:tt).*) => {{
        print!("&({})", stringify!($c));
        for part in [$(stringify!($f)),*] {
            print!(".{{{part}}}");
        }
        println!("");
    }};
}

fn main() {
    project!(&(a).b.c);
    project!(&(get_a()).b.c);
    project!(&(a).b.[0]);
    project!(&(a).b.[1..2]);
}

This prints out:

&(a).{b}.{c}
&(get_a()).{b}.{c}
&(a).{b}.{[0]}
&(a).{b}.{[1 .. 2]}

The problem is that the code you're expanding to is invalid. Your expansion uses (*inner).[0] which isn't valid syntax.

@Hyeonu has already noted what I'd do: just grab the whole field/subscript chain as a blind lump of tokens and substitute it directly into place.

Thanks so much, folks! This works perfectly!

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.