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:
- Having to prefix the arguments with the name of the
struct
(also needing to explicitly include it in the namespace adds some overhead). - Putting
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?
Disclaimer: I advise against using this solution, because the errors reported are horrid. The cleanest solution, codewise, is most probably the builder pattern.
With that out of the way... I whipped together a proof-of-concept demonstrating operator abuse.
Its main advantage over using struct syntax to pass arguments, or using a builder, is that it allows reuse across functions taking different sets of the same parameters.
On the other hand, it does suffer from having to import a whole lot of symbols (each name to be used).
It looks like:
Note that the name is really optional, it merely servers as a builder for the argument itself, but if you already have the argument it can be eschewed. This also means that it's probably not worth creating a name for "obvious" parameters (
Alignment
here).The normal definition of
button
:Requires some augmentation:
And yes, this does mean that you can augment a function/method defined in a 3rd party library.
This is supported by:
Note that this does NOT address:
If a user is patient enough to enumerate all combinations of optional parameters, a solution like @ljedrz is possible:
will then allow all of the following combinations:
The extra set of parentheses is necessary when there is more than one argument.
However there is big downside: the user experience is degraded when using the wrong set of parameters.
The result of calling
p.button("Hello");
is:You can take advantage of the
From
trait; that way you can drop some of the boilerplate:This implementation will work specifically for
(&'static str, Alignment, Shape)
tuples; however, you could additionally implementFrom<&'static str>
that would produce aButton
with the givenlabel
andNone
for the other components: