Macro repetition tricks


#1
macro_rules! foo { ($($x:expr),*) => ({ $(let x_n... = $x;)* }); }

I have a macro in which I wish to assign the result of each of an unknown number of expressions to a binding (a different binding for each, of course). Is this possibly by any means, hacky or otherwise?


#2

What is the reason you want to do this? More specific: What are the bindings for? Do you need to access them afterwards? From a first glance, I’d probably use a tuple as in

let x = (23, 42, 99);
x.0

Accessing the variables won’t work because due to macro hygiene they will be different once expanded to avoid conflicts. If you need to do stuff to the expressions (wrap them, or whatever) you can have the macro just return a tuple.

But as said, all depends on the larger use-case.


#3

No I need to bind them within the macro, not for consumption where the macro is invoked. The macro above was just a toy example of this particular aspect of the problem, the full macro spawns a thread which needs to own the result of these expressions so that it can be repeatedly lend them (possibly mutably) to another function.

You can see the full use case here; currently these require the user of the macro to (arbitrarily) name the bindings, which is very non-ideal.


#4

Ah, so you’re basically after having macro-internal temporaries to pass on to some call? I came up with the following:

macro_rules! mutcall_expand {
    (($next:expr), ($($arg:expr),*)) => {{
        let mut v = $next;
        foo($($arg),*, &mut v)
    }};
    (($next:expr, $($rest:expr),*), ($($arg:expr),*)) => {{
        let mut v = $next;
        mutcall_expand!(($($rest),*), ($($arg,)* &mut v))
    }}
}

macro_rules! mutcall {
    ($($e:expr),*) => { mutcall_expand!(($($e),*), ()) }
}

fn foo(x: &mut i32, y: &mut i32, z: &mut i32) {
    println!("val {:?}", (x, y, z));
}

fn main() {
    mutcall!(23, 42, 99);
}

It basically takes each expression element, creates a temporary to store the value, and passes a &mut reference on in an argument list. Once it reaches the last argument it performs the cal. I haven’t yet been able to make one that works with no values, however. Though you can just intercept that in the outer mutcall! macro by matching (), in case you’re dealing with something having arbitrary arguments like a closure.

I hope this helps.


#5

You can do it by using , as a terminator in the helper macro rather than a separator. You used to be able to use it as a continuation marker (i.e. at the beginning of each element after the first) but the compiler won’t allow you to put the separator for after an expression inside a repetition anymore.

macro_rules! mutcall_expand {
    ((), ($($arg:expr),*)) => {{
        foo($($arg),*)
    }};
    
    (($next:expr, $($rest:expr,)*), ($($arg:expr),*)) => {{
        let mut v = $next;
        mutcall_expand!(($($rest,)*), ($($arg,)* &mut v))
    }};
}

macro_rules! mutcall {
    ($($e:expr),*) => { mutcall_expand!(($($e,)*), ()) }
}

fn foo(x: &mut i32, y: &mut i32, z: &mut i32) {
    println!("val {:?}", (x, y, z));
}

// fn foo() {
//     println!("val {:?}", ());
// }

fn main() {
    mutcall!(2, 4, 6);
}

#6

Of course, recursive looping! Thanks so much.


#7

Excellent, thanks. Very good to know.