简体   繁体   English

Rust 中的默认函数参数

[英]Default function arguments in Rust

Is it possible to create a function with a default argument?是否可以创建一个带有默认参数的函数?

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

Since default arguments are not supported you can get a similar behavior using Option<T>由于不支持默认参数,您可以使用Option<T>获得类似的行为

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

This accomplishes the objective of having the default value and the function coded only once (instead of in every call), but is of course a whole lot more to type out.这实现了让默认值和函数只编码一次(而不是在每次调用中)的目标,但当然还有很多需要输入。 The function call will look like add(None, None) , which you may or may not like depending on your perspective.函数调用看起来像add(None, None) ,您可能喜欢也可能不喜欢,这取决于您的观点。

If you see typing nothing in the argument list as the coder potentially forgetting to make a choice then the big advantage here is in explicitness;如果您发现在参数列表中什么都不输入,因为编码人员可能会忘记做出选择,那么这里的最大优势在于明确性; the caller is explicitly saying they want to go with your default value, and will get a compile error if they put nothing.调用者明确表示他们想要使用您的默认值,如果他们什么都不放,就会得到一个编译错误。 Think of it as typing add(DefaultValue, DefaultValue) .将其视为键入add(DefaultValue, DefaultValue)

You could also use a macro:您还可以使用宏:

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

The big difference between the two solutions is that with "Option"-al arguments it is completely valid to write add(None, Some(4)) , but with the macro pattern matching you cannot (this is similar to Python's default argument rules).两种解决方案之间的最大区别在于,使用 "Option"-al 参数编写add(None, Some(4))是完全有效的,但是使用宏模式匹配则不能(这类似于 Python 的默认参数规则) .

You could also use an "arguments" struct and the From / Into traits:您还可以使用“参数”结构和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)));
}

This choice is obviously a lot more code, but unlike the macro design it uses the type system which means the compiler errors will be more helpful to your library/API user.这个选择显然是更多的代码,但与宏设计不同,它使用类型系统,这意味着编译器错误将对您的库/API 用户更有帮助。 This also allows users to make their own From implementation if that is helpful to them.如果这对他们有帮助,这也允许用户制作自己的From实现。

No, it is not at present.不,目前还没有。 I think it likely that it will eventually be implemented, but there's no active work in this space at present.我认为它最终可能会实施,但目前在这个领域没有积极的工作。

The typical technique employed here is to use functions or methods with different names and signatures.这里采用的典型技术是使用具有不同名称和签名的函数或方法。

No, Rust doesn't support default function arguments.不,Rust 不支持默认函数参数。 You have to define different methods with different names.您必须使用不同的名称定义不同的方法。 There is no function overloading either, because Rust use function names to derive types (function overloading requires the opposite).也没有函数重载,因为 Rust 使用函数名来派生类型(函数重载要求相反)。

In case of struct initialization you can use the struct update syntax like this:在结构体初始化的情况下,您可以使用如下结构体更新语法:

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

[on request, I cross-posted this answer from a duplicated question] [应要求,我从一个重复的问题中交叉发布了这个答案]

Rust doesn't support default function arguments, and I don't believe it will be implemented in the future. Rust 不支持默认函数参数,我不相信它会在未来实现。 So I wrote a proc_macro duang to implement it in the macro form.所以我写了一个proc_macro duang,以宏的形式实现。

For example:例如:

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

If you are using Rust 1.12 or later, you can at least make function arguments easier to use with Option and into() :如果你使用 Rust 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);
}

Another way could be to declare an enum with the optional params as variants, which can be parameterized to take the right type for each option.另一种方法是将带有可选参数的枚举声明为变体,可以将其参数化以针对每个选项采用正确的类型。 The function can be implemented to take a variable length slice of the enum variants.可以实现该函数以获取枚举变体的可变长度切片。 They can be in any order and length.它们可以是任何顺序和长度。 The defaults are implemented within the function as initial assignments.默认值在函数中作为初始分配实现。

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") ] );

}

output:输出:

  name: Bob
weight: 90 kg
height: 1.8 m

Building on previous answers, keep in mind you can create new variables with the same name as existing ones, which will hide the previous one.在以前的答案的基础上,请记住,您可以创建与现有变量同名的新变量,这将隐藏前一个变量。 This is useful for keeping code clear if you don't plan to use the Option<...> anymore.如果您不打算再使用Option<...> ,这对于保持代码清晰很有用。

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

Yes, it is possible to create a function with default argument.是的,可以创建一个带有默认参数的函数。 In the example you provided, the function add takes two integer arguments, a and b, and returns the sum of a and b.在您提供的示例中,函数 add 接受两个整数参数 a 和 b,并返回 a 和 b 的和。 The default values for a and b are 1 and 2. a 和 b 的默认值为 1 和 2。

Here's an example of to use this function:这是使用此功能的示例:

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

println(add())  // would print 3
println(add(5))  // would print 7
println(add(5, 6))  // would prints 11

FYI, the default values for the arguments are only used if the arguments are not provided when the function is called.仅供参考,参数的默认值仅在调用函数时未提供参数的情况下使用。 If an argument is provided, the default value is not used and the provided value is used instead.如果提供了参数,则不使用默认值,而是使用提供的值。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM