是否可以创建一个带有默认参数的函数?
fn add(a: int = 1, b: int = 2) { a + b }
不,目前不是。我认为它最终可能会实施,但目前在这个领域还没有积极的工作。
这里采用的典型技术是使用具有不同名称和签名的函数或方法。
不,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); }
[应要求,我从一个重复的问题中交叉发布了这个答案]
由于不支持默认参数,您可以使用Option<T>获得类似的行为。
Option<T>
fn add(a: Option<i32>, b: Option<i32>) -> i32 { a.unwrap_or(1) + b.unwrap_or(2) }
这实现了只对默认值和函数编码一次(而不是在每个调用中)的目标,但当然还有更多的内容需要输入。函数调用将类似于add(None, None),您可能喜欢它,也可能不喜欢,这取决于您的观点。
add(None, None)
如果您看到在参数列表中没有输入任何内容,因为编码器可能会忘记做出选择,那么这里的最大优势就是明确性。调用者明确地说他们想要使用你的默认值,如果他们什么都不放,就会得到一个编译错误。可以将其视为输入add(DefaultValue, DefaultValue)。
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参数,编写__abc0是完全有效的,但使用宏模式匹配则不能(这类似于Python的默认参数规则)。
您还可以使用“ arguments ”结构和__abc0/__abc1特征:
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实现。
From
如果您使用的是Rust 1.12或更高版本,您至少可以使函数参数更易于使用Option和into():
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); }
Rust不支持默认函数参数,并且我不相信它将来会被实现。 所以我写了一个PROC_的宏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); }
另一种方法是使用可选参数作为变量来声明枚举,可以对其进行参数化,以便为每个选项采用正确的类型。该函数可以实现为采用枚举变体的可变长度切片。它们可以是任何顺序和长度。默认值在函数中作为初始赋值实现。
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本身也可以是可选的。
args
fn foo(args: Option<&[FooOptions]>) { let args = args.or(Some(&[])).unwrap(); // ... }
基于以前的答案,请记住,您可以创建与现有变量同名的新变量,这将隐藏以前的变量。如果您不打算再使用Option<...>,这对于保持代码清晰非常有用。
Option<...>
fn add(a: Option<i32>, b: Option<i32>) -> i32 { let a = a.unwrap_or(1); let b = a.unwrap_or(2); a + b }