Macro conrod_core::builder_methods[][src]

macro_rules! builder_methods {
    (pub $fn_name:ident { $($assignee:ident).+ = Some($Type:ty) } $($rest:tt)*) => { ... };
    ($fn_name:ident { $($assignee:ident).+ = Some($Type:ty) } $($rest:tt)*) => { ... };
    (pub $fn_name:ident { $($assignee:ident).+ = $Type:ty } $($rest:tt)*) => { ... };
    ($fn_name:ident { $($assignee:ident).+ = $Type:ty } $($rest:tt)*) => { ... };
    ($($rest:tt)*) => { ... };
}

A macro to simplify implementation of “builder-pattern” methods.

The Builder Pattern

Conrod (and much of the Rust ecosystem) makes extensive use of the builder pattern in order to provide an expressive widget API. After much iteration, we settled upon the builder pattern as the best approach to interacting with highly optional types, or in our case, widgets.

Almost all widgets implement at least a few methods in order to take advantage of this pattern. We call them “builder methods”.

The builder pattern looks like this:


struct Button {
    color: Option<Color>,
}

impl Button {

    /// Construct a default Button.
    pub fn new() -> Self {
        Button { color: None }
    }
     
    /// A Color "builder method".
    ///
    /// Builds the Button with the given Color.
    pub fn color(mut self, color: Color) -> Self {
        self.color = Some(color);
        self
    }

}

fn main() {
    // Here we build a purple button.
    let purple_button = Button::new().color(LIGHT_PURPLE);
    assert!(button_color(&purple_button) == LIGHT_PURPLE);

    // Here we build a button with some default colour (which in our case is BLACK).
    let button = Button::new();
    assert!(button_color(&button) == BLACK);
}

// A function that returns a button's color or some default if the button's color is `None`.
fn button_color(button: &Button) -> Color {
    button.color.unwrap_or(BLACK)
}

This allows us to support large numbers of optionally specified parameters on widgets, rather than forcing a user to give them all as Option arguments to some function.

builder_method!

This macro allows you to easily implement any number of builder methods for either trait or direct implementations.

Here’s what implementing the color method for our Button now looks like:

impl Button {
    builder_method!(pub color { color = Some(Color) });
}

Breaking it down

  • The first color is an ident which specifies the name of the builder function. The preceding pub visiblity token is optional.
  • The second color is the field of self to which we assign the given value when building.
  • Color is the type which the builder method receives as an argument. The encapsulating Some(*) is optional, and can be removed for cases where the field itself is a normal type and not an Option type.

Multiple builder_methods!

We can also use the macro to implement multiple builder methods at once. The following is an example of this directly from conrod’s Tabs widget implementation. It expands to 9 unique builder methods - one for every line.

builder_methods!{
    pub bar_width { style.maybe_bar_width = Some(Scalar) }
    pub starting_tab_idx { maybe_starting_tab_idx = Some(usize) }
    pub label_color { style.maybe_label_color = Some(Color) }
    pub label_font_size { style.maybe_label_font_size = Some(FontSize) }
    pub canvas_style { style.canvas = canvas::Style }
    pub pad_left { style.canvas.pad_left = Some(Scalar) }
    pub pad_right { style.canvas.pad_right = Some(Scalar) }
    pub pad_bottom { style.canvas.pad_bottom = Some(Scalar) }
    pub pad_top { style.canvas.pad_top = Some(Scalar) }
}

Note that the builder_methods! macro is designed to work harmony with widget_style! - a macro which simplifies implementation of a widget’s associated Style type. If you are designing your own widget and you haven’t looked at it yet, we recommend you check out the docs.