Default argument in Rust

Default argument allows us to call function omitting some paramater. When a argument is omitted provided default paramater is used. While rust does not support default argument we can make use of macro to achive similar functionality.

# Default parameter in general

Default parameter are the implementation in function where default provided value is used when the caller omit the argument. For simple reference, below is the C++ program with default argument

#include <iostream>
using namespace std;
int add(int a, int b = 0, int c = 0){
	return a + b + c;
}
int main(){
	cout << "add(10) is " << add(10) << endl;
	cout << "add(10, 20) is " << add(10, 20) << endl;
	cout << "add(10, 20, 30) is " << add(10, 20, 30) << endl;
}

In above program we were able to omit passing of b and c and when omitted their value is default to 0.

# First attempt in rust

But such syntax is not available in rust. If we wanted to do something like above then first attempt maybe as

// We will be using 0 as default argument
fn add(a: i32, b: i32, c: i32){
   a + b + c
}
fn add_single(a: i32){
   add(a, 0, 0)
}
fn add_double(a: i32, b: i32){
   add(a, b, 0)
}
fn main(){
   println!("add(10) is {}", add_single(10));
   println!("add(10, 20) is {}", add_double(10, 20));
   println!("add(10, 20, 30) is {}", add(10, 20, 30));
}

While above program works as expected. The default argument in above call are set to 0 as written in macro block. however, we have to name our function differently thus need to call function of different name for same operation. You may also define all function with same name as described in Function overloading based on argument count but still we can do it in another way although we will still be using macro.

# Solution

macro_rules! print_this{
   ($a: expr) => {
       print_this_raw($a, 0, 0);
   };
   ($a: expr, $b: expr) => {
       print_this_raw($a, $b, 0);
   };
   ($a: expr, $b: expr, $c: expr) => {
       print_this_raw($a, $b, $c);
   }
}
fn print_this_raw(a: i32, b: i32, c: i32){
   println!("a = {} >> b = {} >> c = {}", a, b, c);
}
fn main(){
   // a = 10, b = 0, c = 0
   print_this!(10);
   // a = 10, b = 20, c = 0
   print_this!(10, 20);
   // a = 10, b = 20, c = 30
   print_this!(10, 20, 30);
}

# Limitation

Although above implementation looks more clean than previous one someone may expect to call function with something like

// a = 0, b = 10, c = 0
print_this!(, 10, )

Here we are omitting a & c but passing b = 10. With above macro this is not possible. Also in general it is only recommended to omit the trailing parameter but not of middle one. However we can still do such by adding branch in macro as

macro_rules! print_this{
   // --- skipped code as above
   //
   (, $b: expr, ) => {
       print_this_raw(0, $b, 0)
   }
}

Similarly we could add arm to enable calls like

// Exercise: Write macro arm to allow calls like below

// a = 0. b = 10, c = 20
print_this!(, 10, 20);
// a = 0, b = 0, c = 10
print_this!(, , 10);

But this will be tedious so it is usually not recommended nor desired to enable omitting parameter and calling function in every possible permutation. Instead just add few arms that makes sense and is common.


You have the power

You can Edit this article or even Submit new articles. Want to Learn more?