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!