Accepting a variable number of arguments
Most of the time, when you want to operate on a dataset, you will design a function that takes a collection. In some cases, however, it is nice to have functions that just accept an unbound amount of parameters, like JavaScript's rest parameters. This concept is called variadic functions and is not supported by Rust. However, we can implement it ourselves by defining a recursive macro.
Getting started
The code in this recipe might be small, but it will look like gibberish if you're not familiar with macros. If you have not yet learned about macros or need a refresh, I recommend that you take a quick look at the relevant chapter in the official Rust book (https://doc.rust-lang.org/stable/book/first-edition/macros.html).
How to do it...
In the
src/bin
folder, create a file calledvariadic.rs
Add the following code and run it with
cargo run --bin variadic
:
1 macro_rules! multiply { 2 // Edge case 3 ( $last:expr ) => { $last }; 4 5 ( $head:expr, $($tail:expr), +) => { 6 // Recursive call 7 $head * multiply!($($tail),+) 8 }; 9 } 10 11 fn main() { 12 // You can call multiply! with 13 // as many parameters as you want 14 let val = multiply!(2, 4, 8); 15 println!("2*4*8 = {}", val) 16 }
How it works...
Let's start with our intention: we want to create a macro called multiply that accepts an undefined amount of parameters and multiplies them all together. In macros, this is done via recursion. We begin every recursive definition with the edge case, that is, the parameters where the recursion should stop. Most of the time, this is where a function call stops making sense. In our case, this is the single parameter. Think about it, what should multiply!(3)
return? It doesn't make sense to multiply it with anything, since we have no other parameter to multiply it with. Our best reaction is to simply return the parameter unmodified.
Our other condition is a match against more than one parameter, a $head
and a comma-separated list of parameters inside of a $tail
. Here, we just define the return value as the $head
multiplied with the multiplication of the $tail
. This will call multiply!
with the $tail
and without the $head
, which means that on every call we process one parameter less until we finally reach our edge case, one single parameter.
There's more...
Keep in mind that you should use this technique sparingly. Most of the time, it is clearer to just accept and operate on a slice instead. However, it makes sense to use this in combination with other macros and higher kinds of concepts where the analogy of a graspable list of things breaks down. Finding a good example for this is difficult since they tend to be extremely specific. You can find one of them at the end of the book though.
See also
- Composing functions recipe inChapter 10, Using Experimental Nightly Features