Rust中的默认函数参数


100

在Rust中是否可以使用默认参数创建函数?

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

4
#6973包含几种解决方法(使用结构)。
休恩2014年

在2020年,如何编码?
puentesdiaz

@puentesdias接受的答案仍然是正确的答案。在Rust中无法做到这一点,您必须编写一个宏,或者使用Option并显式传递None
耶隆

Answers:


55

不,目前不是。我认为它最终可能会实现,但是目前在这个领域还没有积极的工作。

这里采用的典型技术是使用具有不同名称和签名的函数或方法。


2
@ ner0x652:但请注意,这种方法在官方上不建议使用。
克里斯·摩根

@ChrisMorgan您有没有被官方劝阻的消息来源?
Jeroen

1
@JeroenBollen在几分钟的搜索中,我能提供的最好的方法是reddit.com/r/rust/comments/556c0g/…,那里有像brson这样的人,当时是Rust项目的负责人。IRC可能还有更多(不确定)。
克里斯·摩根

107

由于不支持默认参数,因此您可以使用以下类似行为 Option<T>

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

这样可以达到只对默认值和函数进行一次编码(而不是在每次调用中)进行编码的目的,但是要键入的内容当然要多得多。函数调用看起来像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);

两种解决方案之间的最大区别在于,使用“ 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
这个答案最好是几个答案,每种方法一个。我只想投票其中的一个
joel

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_macro duang以宏形式实现它。

例如:

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

如果您使用的是Rust 1.12或更高版本,则至少可以使函数参数更易于与Option和结合使用into()

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

6
尽管从技术上讲是准确的,但Rust社区在这是否是一个“好”主意上一直存在分歧。我个人属于“不好”阵营。
Shepmaster

1
@Shepmaster它可能会增加代码大小,并且不是超级可读。那些反对使用该模式的人吗?到目前为止,我已经发现,在符合人体工程学的API的服务中值得权衡取舍,但会认为我可能会错过其他一些陷阱。
squidpickles

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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.