Better pattern for optional arguments using repetition in macro by example

Hello, everyone!

I want to create macros with optional arguments which will be used to initialize a struct. The pattern that I am using is reproduced below, with the help of optional macro repetitions and shadowing:

#![allow(dead_code)]

#[derive(Debug)]
struct Car {
    name: &'static str,
    max_speed: u32, // km/h
    color: &'static str,
}

macro_rules! new_car {
    ($name:expr $(, max_speed:$max_speed:expr)? $(, color:$color:expr)? ) => {
        {
            // HERE: is there a better pattern?
            let _max_speed = 100; // default
            $(let _max_speed = $max_speed;)?
            let _color = "red"; // default
            $(let _color = $color;)?
            //---------------------------------

            $crate::Car{
                name: $name,
                max_speed:_max_speed,
                color: _color,
            }
        }

    };
}

fn main() {
    println!("{:?}", new_car!("Ferrari"));
    println!("{:?}", new_car!("Lambo1", max_speed: 300));
    println!("{:?}", new_car!("Lambo2", color: "black"));
}

This pattern seems a little bit awkward for me. Do you know any better pattern?

If you've several nominal parameters, it can get easily confusing. So for certain cases you may want a proc macro crate using syn and quote2.

In your case you might want to consider using Default and .. rest in a struct initializer.

fn default<T: Default>() -> T {
   T::default()
}

impl Default for O {
    fn default() -> Self {
        O { ... }
    }
}

O { x: v, ..default() }
2 Likes

I agree, this would probably work quite well:

#![allow(dead_code)]

#[derive(Debug)]
struct Car {
    name: &'static str,
    max_speed: u32, // km/h
    color: &'static str,
}

impl Default for Car {
    fn default() -> Self {
        Self {
            name: "",
            max_speed: 100,
            color: "red",
        }
    }
}

macro_rules! new_car {
    ($name:expr $(, max_speed:$max_speed:expr)? $(, color:$color:expr)? ) => {
        {
            $crate::Car {
                name: $name,
                $(max_speed: $max_speed,)?
                $(color: $color,)?
                ..Default::default()
            }
        }
    };
}

fn main() {
    println!("{:?}", new_car!("Ferrari"));
    println!("{:?}", new_car!("Lambo1", max_speed: 300));
    println!("{:?}", new_car!("Lambo2", color: "black"));
    println!("{:?}", new_car!("Lambo3", max_speed: 300, color: "black"));
}

Playground.

3 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.