Отказа от ответственности: Я советую против использования этого решения, поскольку ошибки сообщенных являются противными. Самое чистое решение, кодовое, скорее всего, это шаблон строителя.
С этим из пути ... Я взбитый вместе доказательства о концепции злоупотребления эксплуатанта.
Главное преимущество использования синтаксиса структуры для передачи аргументов или использования построителя заключается в том, что он позволяет повторно использовать разные функции с разными наборами одних и тех же параметров.
С другой стороны, он страдает от необходимости импортировать множество символов (каждое имя, которое будет использоваться).
Это выглядит следующим образом:
// Rust doesn't allow overloading `=`, so I picked `<<`.
fn main() {
let p = Panel;
p.button(LABEL << "Hello", ALIGNMENT << Alignment::Center);
p.button(LABEL << "Hello", Alignment::Left);
p.button(Label::new("Hello"), Alignment::Left);
}
Обратите внимание, что имя действительно необязательно, она просто серверы в качестве строителя для самого аргумента, но если у вас уже есть аргумент, который может быть избегали. Это также означает, что, вероятно, не стоит создавать имя для «очевидных» параметров (Alignment
здесь).
Нормальное определение button
:
#[derive(Debug)]
struct Label(&'static str);
#[derive(Debug)]
enum Alignment { Left, Center, Right }
struct Panel;
impl Panel {
fn button(&self, label: Label, align: Alignment) {
println!("{:?} {:?}", label, align)
}
}
Требуется некоторое увеличение:
impl Carrier for Label {
type Item = &'static str;
fn new(item: &'static str) -> Self { Label(item) }
}
impl Carrier for Alignment {
type Item = Alignment;
fn new(item: Alignment) -> Self { item }
}
const LABEL: &'static Argument<Label> = &Argument { _marker: PhantomData };
const ALIGNMENT: &'static Argument<Alignment> = &Argument { _marker: PhantomData };
И да, это не означает, что вы можете увеличить функцию/метод, определенный в 3-сторонней библиотеки.
Это подтверждается:
trait Carrier {
type Item;
fn new(item: Self::Item) -> Self;
}
struct Argument<C: Carrier> {
_marker: PhantomData<*const C>,
}
impl<C: Carrier> Argument<C> {
fn create<I>(&self, item: I) -> C
where I: Into<<C as Carrier>::Item>
{
<C as Carrier>::new(item.into())
}
}
impl<R, C> std::ops::Shl<R> for &'static Argument<C>
where R: Into<<C as Carrier>::Item>,
C: Carrier
{
type Output = C;
fn shl(self, rhs: R) -> C {
self.create(rhs)
}
}
Обратите внимание, что это НЕ адрес:
- из порядка передачи аргумента
- необязательных аргументов
Если пользователь достаточно терпелив перечислить все возможные комбинации дополнительных параметров, решение как @ljedrz возможно:
struct ButtonArgs {
label: Label,
align: Alignment,
icon: Icon,
}
impl From<Label> for ButtonArgs {
fn from(t: Label) -> ButtonArgs {
ButtonArgs { label: t, align: Alignment::Center, icon: Icon::Circle }
}
}
impl From<(Label, Alignment)> for ButtonArgs {
fn from(t: (Label, Alignment)) -> ButtonArgs {
ButtonArgs { label: t.0, align: t.1, icon: Icon::Circle }
}
}
impl From<(Label, Icon)> for ButtonArgs {
fn from(t: (Label, Icon)) -> ButtonArgs {
ButtonArgs { label: t.0, align: Alignment::Center, icon: t.1 }
}
}
impl From<(Label, Alignment, Icon)> for ButtonArgs {
fn from(t: (Label, Alignment, Icon)) -> ButtonArgs {
ButtonArgs { label: t.0, align: t.1, icon: t.2 }
}
}
impl From<(Label, Icon, Alignment)> for ButtonArgs {
fn from(t: (Label, Icon, Alignment)) -> ButtonArgs {
ButtonArgs { label: t.0, align: t.2, icon: t.1 }
}
}
затем позволят всем из следующих комбинаций:
fn main() {
let p = Panel;
p.button(LABEL << "Hello");
p.button((LABEL << "Hello"));
p.button((LABEL << "Hello", ALIGNMENT << Alignment::Left));
p.button((LABEL << "Hello", ICON << Icon::Circle));
p.button((LABEL << "Hello", ALIGNMENT << Alignment::Left, ICON << Icon::Circle));
p.button((LABEL << "Hello", ICON << Icon::Circle, ALIGNMENT << Alignment::Left));
p.button(Label::new("Hello"));
p.button((LABEL << "Hello", Alignment::Left, Icon::Circle));
}
Дополнительный набор скобок необходим, когда есть больше чем один аргумент.
Однако есть большой недостаток: пользовательский интерфейс ухудшается при использовании неправильного набора параметров.
Результат вызова p.button("Hello");
является:
error[E0277]: the trait bound `ButtonArgs: std::convert::From<&str>` is not satisfied --> <anon>:124:7
| 124 | p.button("Hello");
| ^^^^^^ the trait `std::convert::From<&str>` is not implemented for `ButtonArgs`
|
= help: the following implementations were found:
= help: <ButtonArgs as std::convert::From<Label>>
= help: <ButtonArgs as std::convert::From<(Label, Alignment)>>
= help: <ButtonArgs as std::convert::From<(Label, Icon)>>
= help: <ButtonArgs as std::convert::From<(Label, Alignment, Icon)>>
= help: and 1 others
= note: required because of the requirements on the impl of `std::convert::Into<ButtonArgs>` for `&str`
Я удалил свой ответ, но все еще кажется, что шаблон строитель может быть использован более элегантно, чем показано в этом вопросе. Если вы интегрируете в API, ваш вызывающий абонент может использовать что-то вроде: 'panel.button (« Test »). Align (Center) .icon (CIRCLE) .make()'. Это в точности эквивалентно Python ключевым словам args (предоставляет значения по умолчанию, позволяет произвольный порядок), и не выглядит неудобно вообще. – user4815162342
Согласитесь, я использовал стиль строителя, и было предложено объявить, что структура может быть альтернативой ... пока я уверен, что некоторые макросы могут избежать некоторых накладных расходов, он выглядит нетривиальным и даже тогда будет неудобным. – ideasman42
Boost.Process (C++) имеет интересное злоупотребление операцией перегрузки. Он создает константу 'label', а затем, когда вы используете' label = "Test", это по очереди создает объект, подобный тому, что будет делать Label {label: "Test"} '. Процесс Boost сочетает это с переменными аргументами, чтобы получить список таких объектов, которые затем дополняются значениями по умолчанию. Вы заканчиваете с 'panel.button (label =" Test ", align = Center, icon = CIRCLE);' вызывает ... однако неясно, как это будет транслироваться в Rust (нужно было бы выбрать другой оператор, чем '=' и, возможно, потребуется передать массив, так как без вариаций?). –