免責事項:報告されたエラーは恐ろしいですので、私は、このソリューションを使用しないことをお勧めします。最も洗練されたソリューションは、コード化されたものが、おそらくビルダーパターンです。道のうちそれと
...私は一緒に、概念実証オペレータ虐待を実証をホイップ。
struct構文を使用して引数を渡すことや、ビルダーを使用することの主な利点は、同じパラメータの異なるセットを使用して関数間で再利用できることです。
一方、シンボル(使用するそれぞれの名前)をたくさんインポートする必要があります。
それは次のようになります。それは、引数自体のビルダーとして、単にサーバ、名前は本当にオプションであることを
// 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 };
そして、はい、これはあなたがサードパーティのライブラリで定義された関数/メソッドを増大させることができることを意味します。これは対応していないことを
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));
}
以上存在するとき、かっこの余分なセットが必要です1つの議論より。
しかし、大きな欠点があります。誤ったパラメータセットを使用すると、ユーザーエクスペリエンスが低下します。
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`
私は私の答えを削除したが、まだBuilderパターンが質問に示されるよりも、よりエレガントに使用することができているようです。 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でどのように変換するのかは不明です( '='バリアントがないので配列を渡す必要があるかもしれません)。 –