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>
fn add(a: Option<i32>, b: Option<i32>) -> i32 {
a.unwrap_or(1) + b.unwrap_or(2)
}
// Usage example:
let result = add(Some(3), Some(4));
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.
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)
.
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).
You could also use an "arguments" struct and the From
/Into
traits:
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. This also allows users to make their own From
implementation if that is helpful to them.
None
to that function to work, can I avoid this?
Commented
Mar 6, 2024 at 9:40
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.
Option<T>
and filling in the default via .unwrap_or_else(|| …)
or similar; or perhaps taking T
and having the caller write T::default()
. If anything, first-class default argument values could be more efficient as that filling of a default value could be reliably shifted to the caller where the compiler reckons it’s worthwhile, without needing to inline the whole thing, resulting in less work being done in cases where a value is provided—though normally the optimiser will make the two approaches much of a muchness.
Commented
Dec 15, 2021 at 1:52
No, Rust doesn't support default function arguments. 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).
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. So I wrote a proc_macro duang to implement it in the macro form.
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()
:
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);
}
impl Into<Option<>>
.
Commented
Jan 25, 2023 at 13:03
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
args
itself could also be optional.
fn foo(args: Option<&[FooOptions]>) {
let args = args.or(Some(&[])).unwrap();
// ...
}
Building on previous answers, keep in mind you can create new variables with the same name as existing ones ("variable shadowing"), which will hide the previous one. This is useful for keeping code clear if you don't plan to use the Option<...>
anymore.
fn add(a: Option<i32>, b: Option<i32>) -> i32 {
let a = a.unwrap_or(1);
let b = a.unwrap_or(2);
a + b
}
There's a default_args
crate for that.
Example:
use default_args::default_args;
default_args! {
fn add(a: int = 1, b: int = 2) {
a + b
}
}
Note that you now call your function with a macro call.
(Ex: add!(12)
)
Ask yourself, why do you want default arguments? There are many answers depending upon your reasons:
If you've far too many arguments, then ideally restructure your code into more different structs, like some builder pattern, maybe via functional record updates (FRU).
pub struct MyFunction { ... many arguments ... }
impl Default for MyFunction { ... }
impl MyFunction { go(self) { ... } }
Invoke like
MyFunction { desired arguments, ..Default::default() }.go()
Your builder could often be some related type, which makes method chaining nicer. In these, you could hide arguments at the type level, assuming users do not embed your intermediate type.
pub struct MyWorker<R: RngCore = OsRng> { ... }
If non-defaults wind up rare, then you could expose the via some trait being used anyways.
In schnorrkel for example, I needed a R: RngCore
argument for test vectors, as well as niche users who wanted derandomized signatures. Yet, I wanted the wider ecosystem to use only well randomized signatures. I'd already adopted the merlin::Transcript abstraction for Fiat-Shamir transforms. I therefore provide only OsRng
via the trait, but you can change the type behind the trait for test vectors or whatever. https://github.com/w3f/schnorrkel/blob/master/src/context.rs#L94-L103
None
to the respective argument(s).
Commented
Mar 6, 2024 at 9:48
I would like to propose this approach
///
/// ## Argument
/// should_eat_veg: Option<bool> **default=false**
///
fn plan_my_meal(should_eat_veg: Option<bool>) -> Meal {
let should_eat_veg = should_eat_veg.map_or(false, |v| v);
...
}
Option<T>
type declaration for argument with default value, pass None
for default
Option
and explicitly passNone
.