7

I've created a Rust macro which expands to a function declaration.

macro_rules! build_fn
{
    ($name:tt) => {

        pub fn $name(&self) -> Result<i32, Box<dyn Error>>
        {
            // <implementation>
            
            Ok(0)
        }
    };
}

Is it possible to expand this so the macro can take variable parameters?

e.g.

($name:tt, /*$variable_args ? */) => {

        pub fn $name(&self, /*$variable_args ? */) -> Result<i32, Box<dyn Error>>
        {
            // ...
            
            Ok(0)
        }
    };
}

Playground

4
  • Repetition? Commented Jan 27, 2022 at 1:00
  • Yes, I think I need something like this $($arg:expr),* .. but I can't quite get the syntax right. It makes me question if it's possible in this context but I confess that macros are certainly my weak point with Rust. Commented Jan 27, 2022 at 1:14
  • 3
    $($arg:expr),* covers expressions, you need names and types for the arguments. You can use something like $($p:pat : $t:ty),* $(,)? for full cover, but if that's at the end, better to use $($args:tt)*. Commented Jan 27, 2022 at 1:18
  • I've made some progress with this but it's still not right for multiple args. Playground Commented Jan 31, 2022 at 22:23

2 Answers 2

7

Since @ranfdev's answer is not currently compiling, I've fixed & simpified & extended it to support multiple types -- and it now runs in Rust stable:

use std::fmt::Write;

macro_rules! build_vararg_fn {
    ($name:tt, $($v:tt: $t:ty),+) => {

        fn $name($($v: $t),+) {
            let mut msg = String::from("args: ");
            $(
                write!(msg, "{:?}, ", $v).unwrap();
            )+
            println!("{}", &msg[..msg.len()-2]);
        }
    }
}

fn main() {
    build_vararg_fn!(test_single_arg, x: i32);
    build_vararg_fn!(test_multi_arg, x: i32, y: i32);
    build_vararg_fn!(test_multi_type, x: i32, y: i32, z: f64);

    test_single_arg(10);
    test_multi_arg(1, 2);
    test_multi_type(1, 2, 3.14159);
}

Output:

args: 10
args: 1, 2
args: 1, 2, 3.14159

See it on Playgroud!

Sign up to request clarification or add additional context in comments.

Comments

5

Indeed, it's possible. You need to expand the parameters as $field: $ty:

use std::error::Error;

macro_rules! build_fn
{
    ($name:tt, $($v:ident: $t:ty),*) => {

        pub fn $name(&self, $($v: $t),*)
        {
            let args = [$($v,)*];
            println!("Args = {:?}", args);
        }
    };
}

struct MyStruct {}

impl MyStruct {
    build_fn!(test_single_arg, x: i32);
    build_fn!(test_multi_arg, x: i32, y: i32);
}

fn main() -> Result<(), Box<dyn Error>> {
    let my_struct = MyStruct {};
    
    my_struct.test_single_arg(10);
    my_struct.test_multi_arg(1, 2);

    Ok(())
}

Link to the playground: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=d798cc6a744b53af47dd8cf40fc3b115

1 Comment

This looked promising, but it doesn't currently compile on the playground. Would you mind updating your answer?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.