Rust中的默认函数参数

i34xakig  于 2022-12-23  发布在  其他
关注(0)|答案(7)|浏览(382)

是否可以创建一个带有默认参数的函数?

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

aor9mmx11#

由于不支持缺省参数,因此可以使用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)
也可以使用宏:
x一个一个一个一个x一个一个二个x
这两种解决方案之间最大的区别在于,使用"Option"-al参数时,编写add(None, Some(4))是完全有效的,但使用宏模式匹配时,就不能这样做了(这类似于Python的默认参数规则)。
你也可以使用一个"arguments"结构和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实现,如果这对他们有帮助的话。

ej83mcc0

ej83mcc02#

不,目前还没有。我认为它最终很可能会得到实施,但目前在这一领域还没有积极的工作。
这里采用的典型技术是使用具有不同名称和签名的函数或方法。

n9vozmp4

n9vozmp43#

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

[on请求,我从重复问题交叉发布了此答案]

kupeojn6

kupeojn64#

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

velaa5lx5#

如果您使用的是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);
}
ctehm74n

ctehm74n6#

另一种方法是将一个带有可选参数的枚举声明为变量,变量可以被参数化,以便为每个选项获取正确的类型。函数可以实现为获取枚举变量的可变长度切片。变量可以是任何顺序和长度。默认值在函数中实现为初始赋值。

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

args本身也可以是可选的。

fn foo(args: Option<&[FooOptions]>) {
    let args = args.or(Some(&[])).unwrap();
    // ...
}
gkl3eglg

gkl3eglg7#

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

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

相关问题