Rust中的默认函数参数

Jer*_*oen 61 parameters arguments function rust

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

fn add(a: int = 1, b: int = 2) { a + b }
Run Code Online (Sandbox Code Playgroud)

amp*_*ron 55

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

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

这实现了将默认值和函数编码一次(而不是在每次调用中)的目标,但当然要输出更多.函数调用看起来像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)
    };
}
Run Code Online (Sandbox Code Playgroud)

两个解决方案之间的最大区别在于,使用"Option"-al参数,它完全有效add(None, Some(4)),但是使用宏模式匹配则不能(这类似于Python的默认参数规则).

  • 如果您提到您更喜欢哪种方法,您的评论会更有用。;-) 我猜这是宏 (14认同)
  • 这个答案最好有几个答案,每种方法一个。我只想投票给其中一个 (3认同)

Chr*_*gan 37

不,它现在不是,虽然我认为它最终可能会实施,但它不太可能在1.0之前出现.

这里采用的典型技术是具有不同签名的不同功能或方法.

  • “我认为它最终可能会被实施”——为什么?它不会增加额外的运行时开销吗?如果 Rust 添加它,这似乎违背了整个“零成本抽象”哲学。 (4认同)
  • @ ner0x652:但请注意,这种做法是正式劝阻的. (2认同)
  • @JeroenBollen 在几分钟的搜索中我能想到的最好的是 https://www.reddit.com/r/rust/comments/556c0g/optional_arguments_in_rust_112/,在那里你有像 brson 这样的人,他是 Rust 项目的负责人当时。IRC 可能有更多,不确定。 (2认同)

eul*_*isk 33

不,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);
}
Run Code Online (Sandbox Code Playgroud)

[根据要求,我从一个重复的问题中交叉发布了这个答案]


小智 11

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);
}
Run Code Online (Sandbox Code Playgroud)


nat*_*nke 11

在以前的答案的基础上,请记住,您可以创建与现有变量同名的新变量,这将隐藏前一个变量。如果您不打算Option<...>再使用它,这对于保持代码清晰很有用。

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


squ*_*les 7

如果你使用 Rust 1.12 或更高版本,你至少可以让函数参数更容易与Optionand一起使用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);
}
Run Code Online (Sandbox Code Playgroud)

  • 虽然技术上准确,但 Rust 社区在这是否是一个“好”主意的问题上存在分歧。我个人属于“不好”的阵营。 (7认同)
  • @Shepmaster 它可能会增加代码大小,并且它不是超级可读的。那些反对使用这种模式吗?到目前为止,我发现在为符合人体工程学的 API 服务方面进行权衡是值得的,但我认为我可能会遗漏其他一些问题。 (2认同)

Tod*_*odd 5

另一种方法是将带有可选参数的枚举声明为变体,可以将其参数化以针对每个选项采用正确的类型。可以实现该函数以获取枚举变体的可变长度切片。它们可以是任何顺序和长度。默认值在函数中作为初始分配实现。

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

}
Run Code Online (Sandbox Code Playgroud)

输出:

  name: Bob
weight: 90 kg
height: 1.8 m
Run Code Online (Sandbox Code Playgroud)