2017-01-13 11 views
3

私はRustのキーワード引数に機能的に類似したものを持っていることに興味があり、現在はサポートされていません。Rustのキーワードスタイル関数の引数を最善*偽装するには?

キーワード引数を提供する言語では

、このようなものが一般的である:これはビルダーパターン、例えばを使用して処理

panel.button(label="Some Button") 
panel.button(label="Test", align=Center, icon=CIRCLE) 

私が見てきた:罰金しかしである

ui::Button::new().label("Some Button").build(panel) 
ui::Button::new().label("Test").align(Center).icon(CIRCLE).build(panel) 

Pythonのキーワード引数と比較して少し厄介です。しかし、ラストでimpl DefaultOption<..>メンバーと構造体の初期化を使用して


が実際にキーワード引数を書くことに似ている何か、などに非常に近い何かを得るために使用することができ

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() 
}); 

これは動作しますが、

  • 引数に接頭辞としての名前を付けることになっています
    (明示的に名前空間に含める必要があるため、オーバーヘッドが追加されます)。
  • オプションの引数の周りにSome(..)を置くことは煩わしい/冗長です。
  • .. Default::default()が少しずつ退屈です。

は、キーワードのアクセスの代替として、より簡単にこの仕事をするために、これらの問題のいくつか、(例えばマクロを使用して)を減らすためにそこに方法はありますか?

+4

私は私の答えを削除したが、まだBuilderパターンが質問に示されるよりも、よりエレガントに使用することができているようです。 APIに統合されている場合、呼び出し側は 'panel.button(" Test ")。align(Center).icon(CIRCLE).make()'のようなものを使用できます。これはPythonのキーワードargs(デフォルト値を提供し、任意の順序を可能にする)とまったく同じであり、全く厄介なものではありません。 – user4815162342

+0

私はビルダースタイルを使用していましたが、構造体が代替であると宣言することを提案しましたが、いくつかのマクロはオーバーヘッドの一部を避けることができますが、それは些細ではないように見えます。 – ideasman42

+1

Boost.Process(C++)には、演算子のオーバーロードに関する面白い濫用があります。定数 'label'を作成し、' label = "Test" 'を使うと' Label {label: "Test"} 'と同様のオブジェクトを作成します。 Boostプロセスはこれを可変引数と組み合わせて、そのようなオブジェクトのリストを取得し、デフォルトのものを補います。あなたは 'panel.button(label =" Test "、align = Center、icon = CIRCLE);'を呼び出しますが、Rustでどのように変換するのかは不明です( '='バリアントがないので配列を渡す必要があるかもしれません)。 –

答えて

1

From traitを利用できます。その方法は、あなたは、定型の一部をドロップすることができます。

use self::Shape::*; 
use self::Alignment::*; 

#[derive(Debug)] 
struct Button { 
    label: String, 
    align: Option<Alignment>, 
    icon: Option<Shape>, 
} 

#[derive(Debug)] 
enum Shape { Circle } 

#[derive(Debug)] 
enum Alignment { Center } 

impl From<(&'static str, Alignment, Shape)> for Button { 
    fn from((l, a, i): (&'static str, Alignment, Shape)) -> Self { 
     Button { 
      label: l.to_owned(), 
      align: Some(a), 
      icon: Some(i) 
     } 
    } 
} 

fn main() { 
    let b: Button = ("button", Center, Circle).into(); 

    println!("{:?}", b); 
} 

この実装は、特に(&'static str, Alignment, Shape)タプルのために動作します。しかし、あなたは、さらに、他のコンポーネントのために与えられたlabelNoneButtonを生成するFrom<&'static str>を実現することができます。

impl From<&'static str> for Button { 
    fn from(l: &'static str) -> Self { 
     Button { 
      label: l.to_owned(), 
      align: None, 
      icon: None 
     } 
    } 
} 

let b2: Button = "button2".into(); 
+0

'Button =(" button "、Center、Circle).into();'は、各引数に名前を付けるキーワードがないので、通常の関数引数のようになります。 これはオプションの引数についてどのように機能しますか?それで 'let b:Button =(" Button "、Center、Circle).into();'と 'let b:Button =(" button ")。 – ideasman42

+0

私はこの機能を含む答えを広げました。 – ljedrz

+0

これは、引数の組み合わせごとに 'from'関数を宣言する必要があることを意味しますか?また、同じ型の2つ以上のオプションの引数があった場合、どうすればよいか分かりません。 – ideasman42

3

免責事項:報告されたエラーは恐ろしいですので、私は、このソリューションを使用しないことをお勧めします。最も洗練されたソリューションは、コード化されたものが、おそらくビルダーパターンです。道のうちそれと


...私は一緒に、概念実証オペレータ虐待を実証をホイップ。

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` 
+0

あなたは、ルスト引数のルックスルーサーです。 :) – user4815162342

+0

@ user4815162342:私は、私が名誉を傷つけたり、侮辱したと思っているのかどうかはわかりません。 –

関連する問題