I'm interested to have something functionally similar to keyword arguments in Rust, where they're currently not supported.
For languages that provide keyword argument, something like this is common:
panel.button(label="Some Button")
panel.button(label="Test", align=Center, icon=CIRCLE)
I've seen this handled using the builder-pattern, eg:
ui::Button::new().label("Some Button").build(panel)
ui::Button::new().label("Test").align(Center).icon(CIRCLE).build(panel)
Which is fine but at times a little awkward compared with keyword arguments in Python.
However using struct initialization with impl Default
and Option<..>
members in Rust could be used to get something very close to something which is in practice similar to writing keyword arguments, eg:
ui::button(ButtonArgs { label: "Some Button".to_string(), .. Default::default() } );
ui::button(ButtonArgs {
label: "Test".to_string(),
align: Some(Center),
icon: Some(Circle),
.. Default::default()
});
This works, but has some down-sides in the context of attempting to use as keyword args:
struct
Some(..)
around every optional argument is annoying/verbose... Default::default()
at the end of every use is a little tedious.Are there ways to reduce some of these issues, (using macros for example) to make this work more easily as a replacement for keyword access?
I think macros are the best solution to this problem. You can use the builder API and provide an easier macro-based sugar for those who dislike the builder pattern. Using the example in the question:
pub enum Shape { Circle }
pub enum Alignment { Center }
pub struct Button();
impl Button {
pub fn new() -> Button {Button()}
pub fn label(self, x: &str) -> Button { self }
pub fn align(self, x: Alignment) -> Button { self }
pub fn icon(self, x: Shape) -> Button { self }
}
macro_rules! button {
( $($i:ident = $e:expr),* ) => {
{
let btn = Button::new();
$(
btn = btn.$i($e);
)*
btn
}
};
}
fn main() {
let my_button = button!(label="hello", align=Alignment::Center, icon=Shape::Circle);
// Equivalent to
// let my_button = Button::new().label("hello").align(Alignment::Center).icon(Shape::Circle);
}