Rustのデフォルトの関数引数


100

Rustでデフォルトの引数を使用して関数を作成することは可能ですか?

fn add(a: int = 1, b: int = 2) { a + b }

4
6973には、いくつかの回避策が含まれています(構造体を使用)。
huon 2014年

2020年に、どのようにコーディングできますか?
puentesdiaz

@puentesdias受け入れられた答えはまだ正しい答えです。Rustでそれを行う方法はありません。マクロを作成するか、を使用Optionして明示的に渡す必要がありNoneます。
ジェロエン

回答:


55

いいえ、現在はありません。最終的には実装される可能性が高いと思いますが、現時点ではこの分野での活発な作業はありません。

ここで採用されている典型的な手法は、名前とシグネチャが異なる関数またはメソッドを使用することです。


2
@ ner0x652:ただし、このアプローチは公式には推奨されていないことに注意してください。
クリスモーガン

@ChrisMorgan公式に落胆している原因はありますか?
Jeroen 2017年

1
@JeroenBollen数分で検索できる最高のものはreddit.com/r/rust/comments/556c0g/…で、当時Rustプロジェクトリーダーだったbrsonのような人がいます。IRCにはもっとあったかもしれませんが、確かではありません。
クリスモーガン

107

デフォルトの引数はサポートされていないため、を使用して同様の動作を得ることができます Option<T>

fn add(a: Option<i32>, b: Option<i32>) -> i32 {
    a.unwrap_or(1) + b.unwrap_or(2)
}

これにより、デフォルト値と関数を(すべての呼び出しではなく)1回だけコーディングするという目的が達成されますが、もちろん、入力する必要はありません。関数呼び出しはのようadd(None, None)になりますが、視点によっては気に入るかどうかはわかりません。

コーダーが選択を忘れる可能性があるため、引数リストに何も入力していない場合、ここでの大きな利点は明示性にあります。呼び出し元は、デフォルト値を使用することを明示的に言っており、何も入力しないとコンパイルエラーが発生します。入力と考えてくださいadd(DefaultValue, DefaultValue)

マクロを使用することもできます。

fn add(a: i32, b: i32) -> i32 {
    a + b
}

macro_rules! add {
    ($a: expr) => {
        add($a, 2)
    };
    () => {
        add(1, 2)
    };
}
assert_eq!(add!(), 3);
assert_eq!(add!(4), 6);

2つのソリューションの大きな違いは、「Option」-al引数を使用すると、書き込むことは完全に有効ですadd(None, Some(4))が、マクロパターンマッチングを使用する場合はできません(これはPythonのデフォルトの引数ルールに似ています)。

「引数」構造体とFrom/Into特性を使用することもできます。

pub struct FooArgs {
    a: f64,
    b: i32,
}

impl Default for FooArgs {
    fn default() -> Self {
        FooArgs { a: 1.0, b: 1 }
    }
}

impl From<()> for FooArgs {
    fn from(_: ()) -> Self {
        Self::default()
    }
}

impl From<f64> for FooArgs {
    fn from(a: f64) -> Self {
        Self {
            a: a,
            ..Self::default()
        }
    }
}

impl From<i32> for FooArgs {
    fn from(b: i32) -> Self {
        Self {
            b: b,
            ..Self::default()
        }
    }
}

impl From<(f64, i32)> for FooArgs {
    fn from((a, b): (f64, i32)) -> Self {
        Self { a: a, b: b }
    }
}

pub fn foo<A>(arg_like: A) -> f64
where
    A: Into<FooArgs>,
{
    let args = arg_like.into();
    args.a * (args.b as f64)
}

fn main() {
    println!("{}", foo(()));
    println!("{}", foo(5.0));
    println!("{}", foo(-3));
    println!("{}", foo((2.0, 6)));
}

この選択は明らかにはるかに多くのコードですが、マクロ設計とは異なり、型システムを使用します。これは、コンパイラエラーがライブラリ/ APIユーザーにとってより役立つことを意味します。これにより、ユーザーFromが役立つ場合は、ユーザーが独自の実装を行うこともできます。


2
この回答は、アプローチごとに1つずつ、いくつかの回答としてより適切です。私はそれらのちょうど1 upvoteしたい
ジョエル

56

いいえ、Rustはデフォルトの関数引数をサポートしていません。異なる名前で異なるメソッドを定義する必要があります。Rustは関数名を使用して型を導出するため、関数のオーバーロードもありません(関数のオーバーロードには逆が必要です)。

構造体の初期化の場合、次のような構造体の更新構文を使用できます。

use std::default::Default;

#[derive(Debug)]
pub struct Sample {
    a: u32,
    b: u32,
    c: u32,
}

impl Default for Sample {
    fn default() -> Self {
        Sample { a: 2, b: 4, c: 6}
    }
}

fn main() {
    let s = Sample { c: 23, .. Sample::default() };
    println!("{:?}", s);
}

[リクエストに応じて、重複した質問からこの回答をクロスポストしました]


4
これは、デフォルトの引数に非常に役立つパターンです。高くする必要があります
ベン

9

Rustはデフォルトの関数引数をサポートしておらず、将来実装されるとは思わない。そこで、マクロ形式で実装するためにproc_macroduangを作成しました。

例えば:

duang! ( fn add(a: i32 = 1, b: i32 = 2) -> i32 { a + b } );
fn main() {
    assert_eq!(add!(b=3, a=4), 7);
    assert_eq!(add!(6), 8);
    assert_eq!(add(4,5), 9);
}

7

後で錆1.12以上を使用している場合は、少なくともメイク関数の引数より簡単にして使用することができますOptioninto()

fn add<T: Into<Option<u32>>>(a: u32, b: T) -> u32 {
    if let Some(b) = b.into() {
        a + b
    } else {
        a
    }
}

fn main() {
    assert_eq!(add(3, 4), 7);
    assert_eq!(add(8, None), 8);
}

7
技術的には正確ですが、Rustコミュニティは、これが「良い」アイデアであるかどうかについて声に出して分かれています。私は個人的に「良くない」キャンプに陥ります。
シェップマスター2017

1
@Shepmasterはコードサイズを大きくする可能性があり、非常に読みやすくありません。それらはそのパターンを使用することに反対ですか?これまでのところ、人間工学に基づいたAPIのサービスではトレードオフに価値があることがわかりましたが、他のいくつかの落とし穴がない可能性があると考えています。
squidpickles 2017

2

別の方法は、オプションのparamsをバリアントとして列挙型を宣言することです。これは、各オプションに適切な型をとるようにパラメーター化できます。この関数は、列挙型バリアントの可変長スライスを取得するように実装できます。それらは任意の順序と長さにすることができます。デフォルトは、初期割り当てとして関数内に実装されます。

enum FooOptions<'a> {
    Height(f64),
    Weight(f64),
    Name(&'a str),
}
use FooOptions::*;

fn foo(args: &[FooOptions]) {
    let mut height   = 1.8;
    let mut weight   = 77.11;
    let mut name     = "unspecified".to_string();

    for opt in args {
        match opt {
            Height(h) => height = *h,
            Weight(w) => weight = *w,
            Name(n)   => name   =  n.to_string(),
        }
    }
    println!("  name: {}\nweight: {} kg\nheight: {} m", 
             name, weight, height);
}

fn main() { 

            foo( &[ Weight(90.0), Name("Bob") ] );

}

出力:

  name: Bob
weight: 90 kg
height: 1.8 m
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.