After lots of thinking over the years and reviewing prior discussions some time last year, I would like to propose two independent RFCs for function parameter defaults, and named function parameters. Due to time constraints I expect it will take me at least a couple more months to write the other one, but I feel like the first one is now ready enough for sharing as a pre-RFC:
- Feature Name:
function_param_defaults - Start Date: 2026-02-14
Summary
Add support for default values of individual parameters in function declarations.
impl TcpStream {
pub fn set_nonblocking(
&self,
nonblocking: bool = true,
) -> io::Result<()> {
self.0.set_nonblocking(nonblocking)
}
}
// these two calls are equivalent
tcp_stream.set_nonblocking()
tcp_stream.set_nonblocking(true)
Motivation
In a lot of existing code, there are function parameters that exist to control some detail of that function's behavior. Commonly, these are Option<T>s, builder-like Options structs, enums, or bitflags-enabled flag structs. Many of these types have a default value, but it still has to be supplied explicitly, or there have to be multiple "versions" of the function for different amounts of arguments.
For a real-world example, these are the constructors of pulldown_cmark::Parser, at the time of writing:
impl<'input> Parser<'input, DefaultBrokenLinkCallback> {
pub fn new(text: &'input str) -> Self {
// ...
}
pub fn new_ext(text: &'input str, options: Options) -> Self {
// ...
}
}
impl<'input, F: BrokenLinkCallback<'input>> Parser<'input, F> {
pub fn new_with_broken_link_callback(
text: &'input str,
options: Options,
broken_link_callback: Option<F>,
) -> Self {
// ...
}
}
With this feature, the first two could be combined to
impl<'input> Parser<'input, DefaultBrokenLinkCallback> {
pub fn new(
text: &'input str,
options: Options = Options::empty(),
) -> Self {
// ...
}
}
(with one of the future possibilities regarding defaults on generic parameters, plus named parameters, the new_with_broken_link_callback use case could also be supported by new in a very nice way)
Guide-level explanation
A function's parameters may optionally have a default value:
fn foo(arg1: &str, arg2: u32 = 0) {}
This parameter may be omitted when the function is called, leading to the default being used:
foo("hello");
// is equivalent to
foo("hello", 0);
The default value must be an expression that can be evaluated in a const context.
// compiler error: `std::env::args()` is not a const expression
fn with_env_args(args: std::env::Args = std::env::args()) {}
Any parameters following the first with a default value must have default values themselves.
fn wrong_way_around(first: Option<String> = None, second: u32) {}
The arguments in a call to a function with parameter defaults are in-order, so the following is not possible:
fn with_defaults(a: bool = false, b: u8 = 255) { /* ... */ }
fn call_fn_with_defaults() {
// compile error: expected bool, found u8
with_defaults(0u8);
}
A function with one or more parameter defaults implements the Fn* traits multiple times. That is,
fn with_defaults(a: bool = false, b: u8 = 255) -> i16 { /* ... */ }
fn use_fn_no_params<T>(f: impl FnOnce() -> T) {
// ...
}
fn use_fn_bool_to_i16(f: impl FnMut(bool) -> i16) {
// ...
}
fn use_generic_two_params_to_i16<T, U>(f: impl Fn(T, U) -> i16) {
// ...
}
fn pass_fn_with_defaults() {
// all of these compile
use_fn_no_params(with_defaults);
use_fn_bool_to_i16(with_defaults);
use_generic_two_params_to_i16(with_defaults);
}
Equally, with_defaults from the example above could be cast to any of
fn() -> i16, fn(bool) -> i16 or fn(bool, u8) -> i16.
Defaults for parameters of generic type must be generic themselves that is the following is valid:
fn with_generic_default<T>(value: Option<T> = None) {}
… but this isn't:
// compile error: type mismatch, expected T, found bool
fn with_default<T>(value: T = false) {}
Reference-level explanation
A function's parameters may optionally be suffixed with = <default value> after the type.
This is only supported for the Rust ABI, i.e. extern "C" fn may not specify a default value.
Function parameters inside traits can also have a default value.
In this case, all impl blocks for the trait must repeat the same default value for clarity.
to be extended
Drawbacks
More language complexity.
Rationale and alternatives
to be written
Prior art
to be written
Unresolved questions
None right now.
SemVer considerations
Adding a default to an existing parameter outside a trait
… is a minor change because it can break type inference.
Adding a new parameter with a default outside a trait
… is also a minor change because it can break type inference.
Adding a default to an existing parameter inside a trait
… is a major (breaking) change because implementers must repeat the same default value in their function signature for clarity. This requirement could be relaxed in the future.
Adding a new parameter with a default inside a trait
… is a major (breaking) change because implementers must be updated accordingly.
Removing a default from a parameter
… is a major (breaking) change because callers that don't supply a value for it will be broken.
Changing a function parameter with default from concrete to generic
… is a major (breaking) change because it breaks calls that make use of the default (type inference will not be able to figure out the type). This is unlike the same change for a parameter without a default, which is declared as a minor change at the time of writing.
Future possibilities
- Closures with parameter defaults
- Named parameters (I intend to write a separate RFC for this)
- Allow parameters of generic type to have a non-generic (or less generic) default, i.e.
fn with_default<T>(value: impl IntoIterator<T> = Vec::new()) {} fn call_it() { with_default::<u8>(); // equivalent to with_default(Vec::<u8>::new()); } - Allow trait implementations to not repeat the default value from the trait definition (have the default be implied)
- Would make it no longer a breaking change to add a default to a trait, unless the next point is also implemented
- Allow trait implementations to have a default when the trait definition doesn't (as a type of refinement)
History
- 2026-02-16: Added a paragraph + example to clarify that arguments must still be in-order when calling a function with multiple defaults