Wishlist: functions with keyword args, optional args, and/or variable-arity argument (varargs) lists

A portion of the community (and of the core team) sees one or more of the following features as important for programmer ergonomics:

keyword-based parameters (as opposed to position-based parameters),
optional parameters (where one provides a default value for them, usually in the parameter list),
variable-arity functions (which can be seen as a generalization or variation on optional parameters, depending on how you look at it).

This issue is recording that we want to investigate designs for this, but not immediately. The main backwards compatibility concern is about premature commitment to library API's that would be simplified if one adds one or more of the above features. Nonetheless, We believe we can produce a reasonable 1.0 version of Rust without support for this.
(This issue is also going to collect links to all of the useful RFC PR's and/or Rust issues that contain community discussion of these features.)

See the "Struct sugar" RFC for another take.

I'd like to see some of these RFCs revived in the near future, if someone has time to do so.

Agreed, there's a whole bunch of different keyword arguments proposals floating around and there's been a few discussions which seemed to die off a few months ago... would love to hear the current standpoint on this.

Ok, 1.0 released, even more, can we please  discuss it again? especially default arguments.

This issue is open, it's free to discuss.

This issue is open, it's free to discuss.

(though its possible an internals post might be a better UI for undirected discussion ... we didn't have the discuss forums when we set up these postponed issues...)

I'd love to see keyword arguments. I opened a thread on /r/rust before finding this RFC, which may be an appropriate place for "undirected discussion" too.

In any case, this should be done in such a manner that it does not cause very inconsistent libraries, perhabs by letting the named parameters be optional? For example the names could be given by the argument name. Such that, the function:
fn func(a: u8, b: u8) -> u8;

can be called both with and without named parameters, for example:
func(a: 2, b: 3)

or something along this, while still being able to do:
func(2, 3)

Also, this feature could easily be misused by taking named parameters instead of structs, which, I think, is a bad thing.

Also why would anyone prefer variadic functions over arrays (when type level integers is added, obviously)?

I think it's because supposedly people are thinking about some kind of heterogenous variadicity (like the case of println, which is currently done with macros), and that isn't possible with arrays.

I see, but that's why we got macros. If you want heterogenous variadicity, you gotta go with macros, after all the Rust macro system is very powerful.

I agree, macros are appropriate for this.

When I was first learning Rust pre-1.0, hearing that keyword arguments were not going to be implemented for 1.0 seemed like a terrible choice. From a background in Ruby/Python/JavaScript, keyword arguments are a very common and natural way to create nice APIs.
However, after actually using Rust for a while, I've actually grown to like the struct/builder pattern much more than keyword arguments. The problem with making keyword arguments easy is that it encourages functions that do too much by taking a bunch of different options.
If, instead, you are restricted to positional arguments, then you're more inclined to keep functions small, with a smaller surface area for their signatures. If there a small number of variations on the behavior of a function, these can exist as different named functions (e.g. alternate constructors.) If there really are a large number of variables needed for a function, pulling those variables out into a dedicated struct provides basically all the benefits of keyword arguments, plus the possibility of reuse.
I think if keyword arguments are added, they should just be syntactic sugar for functions that take a struct argument.

If you want heterogenous variadicity, you gotta go with macros, after all the Rust macro system is very powerful.

And then you have to limit the length of your heterogeneous lists to ~10 to avoid horrible metadata bloat.
Macros help, but it's still a half-measure.

@jimmycuadra I agree. Functions should be small and compact, however sometimes you want to be able to know the argument order without looking up the signature (which keyword arguments solve), even though structs and other type constructions should always be prefered over named parameters, when dealing with larger amount of inputs.
One thing that's important to me is consistency with the current call syntax (such that it's up to the caller whether to use named arguments or not). This can be achieved in the way I described in my previous comment.

@petrochenkov The problem you outline is a problem with macros, not a need for method variadicity, I think.

Also, this feature could easily be misused by taking named parameters instead of structs, which, I think, is a bad thing.

No, using structs instead of named arguments is a bad thing. And using array instead of arguments is a horrible thing.

I agree, macros are appropriate for this.

For named arguments? Or for optional arguments? Writing macros for EACH function, export/import it - it's very over-verbose.

I think if keyword arguments are added, they should just be syntactic sugar for functions that take a struct argument.

No way. Better not implement it at all than this.

Functions should be small and compact

Let each user decide how their functions should look. Don't roll into Go-ideology "we don't have it because you don't need it and we know better".

Really frustrated to see such comments. All modern languages except Go has it, but Rust "don't need it, because macros".

No, using structs instead of named arguments is a bad thing.

Not true. Structs are strongly typed (hence less prone to errors) and makes it possible to efficiently reuse codes. Your functions shouldn't take a large number of arguments anyway.

And using array instead of arguments is a horrible thing.

This is not true. Using arguments instead of arrays is a horrible thing.

For named arguments? Or for optional arguments? Writing macros for EACH function, export/import it - it's very over-verbose.

It not very often you run into lack of variadic functions, and when you do, you can write a macro, and when generic integers lands, you can just use arrays. Often functions takes a fixed number of arguments anyways.
Also, note that the macro syntax will soon be revised.

Let each user decide how their functions should look. Don't roll into Go-ideology "we don't have it because you don't need it and we know better".

One of Rust's design philosophies is that Rust should not lead the user into bad code, bugs, and logic errors (this is the reason why we don't have increments, for example). We're all lazy after all, but there is no reason to encourage being lazy.

Your functions shouldn't take a large number of arguments anyway.

I will decide it, not you.

Structs are strongly typed

function arguments too.

Often functions takes a fixed number of arguments anyways.

If your functions often take fixed number of arguments, all other users should obey?

One of Rust's design philosophies is that Rust should not lead the user into bad code

And again - what is bad code will decide user, not some very arrogant community members.

If your functions often take fixed number of arguments, all other users should obey?

I'm just saying that introducing syntactic sugar for such a small special case is not worth it. This job is better suited for macros.

And again - what is bad code will decide user, not some very arrogant community members.

Sure, user should not be forced to not writing bad code, but neither should they be encouraged to.

Just a friendly reminder everyone: let's try to keep conversation civil and constructive! Thanks.

Are keyword-based arguments must be mandatory for all or it is up to me?

@KalitaAlexey I'm not exactly sure what you're asking, but I supose you mean "Is it mandatory to provide the name of the parameters to the function, if we get keyword arguments". And the answer is no, because that would break backwards compatibility.

So. With keyword arguments there is should be possibility to write
fn addObject(object: Object, flag: bool);
fn addObject(object: Object, size: usize);

Is it?

Sure, user should not be forced to not writing bad code, but neither should they be encouraged to.

And there is no = sign between "optional arguments", "named arguments" and "bad code". Maybe some personal opinions only. For somebody 3 arguments it's too much, for somebody it's ok. Nobody should be forced to ask "can I use 4 arguments in this function please?".

I guess this issue is about how to add these features properly or conclude that there is no proper solution, rather than debate if we support them or not.

Just a friendly reminder everyone: let's try to keep conversation civil and constructive! Thanks.

+1. It's incredibly easy to start discrediting ideas when they come from a rude source.
After using OCaml for a few months, I'm a huge fan of keyword arguments. I have a lint for when keyword arguments are used positionally, and I'd be a fan of allowing something similar in Rust.

If you want heterogenous variadicity, you gotta go with macros, after all the Rust macro system is very powerful.

@Ticki This is definitely how it is now, but I haven't seen an inherent argument for keeping it this way. That said, in my experience, variadic functions are few and far between, so I don't necessarily feel that the relatively incremental gain of variadic functions is worth the extra language and compiler complexity. The only difference to the user is a !. It is a little heavier on the implementation side, but not by a huge amount, and I've heard whispers of a macro system overhaul that might even make this nicer?

Let each user decide how their functions should look. Don't roll into Go-ideology "we don't have it because you don't need it and we know better".

@e-oz I agree with this. I feel it both condescending and incredibly frustrating when a language or library does not support something because in the past, the authors decided what a user should and shouldn't do. Obviously Rust enforces lots of things that users shouldn't do, but it's pretty good about giving escape hatches to users who say "no, I really do want to do this".

Functions should be small and compact, however sometimes you want to be able to know the argument order without looking up the signature (which keyword arguments solve)

@Ticki This is to me one of the huge benefits of keyword arguments. It's easier for not only the writer, but the reader of code later! It also makes it harder to accidentally pass same-typed arguments in the wrong order.

...even though structs and other type constructions should always be prefered over named parameters, when dealing with larger amount of inputs.

@Ticki I don't see why this is true? Structs and builders are a lot more boilerplate for the same functionality as keyword + optional arguments.

Also, this feature could easily be misused by taking named parameters instead of structs, which, I think, is a bad thing.

@Ticki One lovely place to use keyword arguments is in constructors. It means the struct internals can remain private, while having all the nicety of position-independent labeled struct creation.

The problem with making keyword arguments easy is that it encourages functions that do too much by taking a bunch of different options.

@jimmycuadra This is people using a useful tool for poor reasons, and I don't see it as a reason to keep the tool out of the language.

@bfops Great comment.

@Ticki I don't see why this is true? Structs and builders are a lot more boilerplate for the same functionality as keyword + optional arguments.

The reason I think struct should be prefered over named arguments is that:
You're able to define methods on struct. You cannot define methods on named parameters (as a collection), due to their "untyped" nature. One thing you might do is to begin to pass what should be a struct as parameters (since this is easy and ergonomical with named parameters).
That said, I'm not against nor supporting it, I'm just providing cases for/against the proposed features.

You're able to define methods on struct. You cannot define methods on named parameters (as a collection), due to their "untyped" nature. One thing you might do is to begin to pass what should be a struct as parameters (since this is easy and ergonomical with named parameters).

@Ticki You can define closures locally, which is essentially equivalent, no? And it only borrows the arguments it actually refers to - with a struct, you can't move/borrow individual members without moving/borrowing all.. I think?

You can define closures locally, which is essentially equivalent, no?

I'm not sure what you're trying to say here. Closures are just traits.

And it only borrows the arguments it actually refers to - with a struct, you can't move/borrow individual members without moving/borrowing all.. I think?

True.
I think you misunderstood my comment (and it's my fault, I'm horrible at wording my thoughts).
The individual parameters are obviously still typed, but the collection of arguments is not. This is not a problem when there's only 4 arguments. But it is when there are more, which is often the case when you used named parameters.
The nature of the parameters are the same in both cases (named or tuple parameters), but it gets problematic when there are many arguments.
Too many arguments is often a sign of using too few data structures, which, in worst case, can lead to spaghetti code (because of rewriting chunks of code which could just be defined as methods on your struct).
Not sure if any of that made sense?

Ah, so it sounds like what you're saying is not that a function with many parameters is necessarily better as a struct, but rather that it's likely that some of those arguments probably make sense to group into a struct?

I'm not sure what you're trying to say here. Closures are just traits.

By this I meant
struct FooParams {
  x: int,
  y: &'static str,
  z: char,
}

impl FooParams {
  fn bar() -> float { ... }
}

fn foo(params: FooParams) {
   ..
}

is roughly equivalent to
fn foo(x: int, y: &'static str, z: char) {
  let bar = || {
    ...
  };
  ...
}

fn foo(x: int, y: &'static str, z: char...) {

}

I ask again. Currently there is no ability to declare two functions with same name. I cannot do
    fn add(&mut self, object: Object);
    fn add(&mut self, boxed_object: Box<Object>);

If we will support keyword-based arguments then it will be allowed?

@KalitaAlexey No, that's out of the scope of this issue.

@ticki So why then have keyword-based arguments?

@KalitaAlexey because that’s the most natural way to call a function with multiple optional arguments which can be confused in order. e.g. OpenOptions:
instead of doing:
let f = OpenOptions::new()
//	.read(true)
//	.write(true)
//	.append(true)
//	.truncate(true)
//	.create(true)
	.open("path/to/file");

//or the shorthands:

let f2 = File.open("path/to/file2");
let f3 = File.create("path/to/file3");

the API could have been
let f2 = File::open("path/to/file2", read = true);
let f3 = File::open("path/to/file3", write = true, create = true, truncate = true);

I'll write up a RFC on default struct fields, which I believe solves both the problem with default parameters and named parameters.

I don't feel that default struct fields address named parameters in satisfying way. Structs add a lot more boilerplate than "first-class" named parameters do. On the other hand, having named + default parameters gives us default struct fields "for free", since Foo::new can now have named and default parameters.

@bfops I would argue that extending the struct builders for supporting default fields would make structs able to replace named parameters, because you'd then be able to leave fields out giving them a default value.

@Ticki please stop. It's not thread "do we need it or not". If you don't need it - just don't write anything.

i’m sorry but i have to agree with @e-oz. this discussion is misplaced. we’re not talking about structs here.

@Ticki You can already do that in a struct builder, by making the field an option, and initializing it when the builder is "invoked" to create the struct.
My argument is that structs and builders are a lot more boilerplate and heavily disincentivize using named parameters entirely, especially for small functions. As you've said, small functions are much more desirable and common, and making those functions pleasant is a huge priority for me personally.

You can already do that in a struct builder, by making the field an option, and initializing it when the builder is "invoked" to create the struct.

Then you get a performance penality and you'd have to handle the fallback value yourself.

@flying-sheep The issues are orthogonal, and one might be able to replace the other. Sorry for hijacking this thread.

When you say "struct builders", are you talking about the builder pattern, or the struct literal syntax? If you're talking about augmenting the struct literal syntax with default values, then I'd still definitely endorse keyword and default arguments, since this is traditionally the kind of behavior that constructors are meant to handle (and using constructors allows the author to keep struct members private). It sounds like both these options will solve the same set of problems, and keyword and default arguments will lessen the overall amount of boilerplate, since it doesn't force users to always use structs.
Whether it's good to force users to always use structs is a matter of opinion, but this seems to me like arguing against tuples because "users should be using a struct". I would prefer to provide both tools and let users make informed decisions based on their use case.

@bfops What I'm talking about is
struct Foo {
    a: u32 = 300, // <- the `300` is the default value of the field a
    b: i32,       // This field do not have a default value
    c: i32,       // Neither do this
}

fn my_function() {
    let var = Foo {
        // Note that I do not define the value of `a` here
        b: 42,
        c: 999,
        ... // Fill the rest of the struct with the default values
    };
    assert_eq!(var.a, 300);
}

or something like that.

OK, so you’re basically saying that with enough syntactic sugar, structs can become simple enough that they are able to make default/named parameters superfluous?
i think there are several problems, the biggest of which is imho: there’s no clear relation between the function and its param struct
sure, you can create fn foo and struct FooParams, but you’ll still have to define both separately, users have to import both separately, and you have to spell it out.
use fooer::{foo,FooParams};

foo(FooParams {...});

just doesn’t have the same simplicity as
use fooer::foo;

foo();

Agreed, both foo(FooParams {...}) and OpenBuilder().this().that().whatever() look like hacks around the fact that there's no support for optional/default arguments.

Agreed, both foo(FooParams {...}) and OpenBuilder().this().that().whatever() look like hacks around the fact that there's no support for optional/default arguments.

You could look at this from different direction where languages are not expressive enough to express the builder pattern in a comfortable way. I personally really enjoy using the builder pattern most of the time (although it does have some quirks regarding finalizers) and think it is strictly more expressive (you can’t express e.g. mutually exclusive options with kwargs).

I find the builder patterns very nice to work with too.

I think Builder is the most ugly thing I ever seen. First time I thought it's some piece of brainfuck code.

you know what? why not combine this stuff?
add a trait Params: Default and syntactic sugar for invoking functions that have a Params-implementing last parameter.
also add syntactic sugar for automatically defining an anonymous params struct.
e.g.:
#[derive(Params,Default)]
struct ExplicitParams {
    herp: u8,
    derp: i8,
}

//use when you have many parameters
fn explicit(durr: &str, named: ExplicitParams) { ... }

//use when you only have few parameters
fn implicit(foo: &str = "xy", bar = 1u8) { ... }

fn main() {
    explicit("unnamed", derp = 2);
    //desugared: explicit("unnamed", ExplicitParams { derp: 2, ..Default::default() })
    implicit(bar = 2);
}

I'm pretty new to rust, so I haven't built complex macros, but I've seen others build really complex and powerful macros (even making a python like list comprehension in rust!)
Is this something macros can already do and, if not, isn't something like default/keyword args really in the domain of macros? It seems like the exact sort of thing macros are intended to handle.
If it were macros, it would be great to have an easy way to declare it, maybe something like:
#[kwargs(y=5, z=MyStruct{x: 4, y: 7})]
fn myfunc(x: u32, y: u32, z: MyStruct) { ... }

This would wrap myfunc with a macro, so you could call it like this:
myfunc(1, 2, MyStruct{x:3, y:85}) # must specify all values

or like this
myfunc!(1, y=72)  # don't need to specify y or z, y overridden here

This seems like the easiest way to be orthogonal to other rust features.

@e-oz

I think Builder is the most ugly thing I've ever seen. First time I thought it's some piece of brainfuck code.

I encourage you to read the Rust Code of Conduct, in particular these two lines:

Please be kind and courteous. There's no need to be mean or rude.
Respect that people have differences of opinion and that every design or implementation choice carries a trade-off and numerous costs. There is seldom a right answer.

This comment does neither of these things. Please be respectful to others.

@vitiral that is my opinion, so please follow second rule from quotation.

Is this something macros can already do and, if not, isn't something like default/keyword args really in the domain of macros? It seems like the exact sort of thing macros are intended to handle.

i don’t think so. i think

syntax support is better for static analysis / doc generation
using macros would strongly increase the number of macro invocation you see (1 per function call), which would be inflationary.

when i see a macro invocation, i know that more is going on than a simple function call, and there are mostly only few macros. if you now couldn’t learn the 1-4 macros of a library anymore because every function became one, that would be bad.

What I am seeking for is the Qt binding in rust (mainly Qt widget rather than qml). It seems that such thing won't happen unless this issue is solved.  Obviously the builder/macro would be too burdensome for this application.  IMO a language cannot wrap Qt is not very "multi-paradigm".

when i see a macro invocation, i know that more is going on than a simple function call

I guess that was my whole point -  kwargs are always going to have more going on than a function call, aren't they? The creation of variables you don't see is going to take up more bytes in the binary certainly.  I have a hard time believing they are ever a "zero cost abstraction", except in the case where the function is inlined.
Also, it would only increase the number of macro creations / invocations when you wanted kwargs. In my opinion kwargs are little more than convenience (and coming from python, they are VERY convenient), which is what macros are supposed to do.
As for static analysis, I think an analyzer could follow all uses that use the kwargs attribute I suggested, certainly.

@zhou13 why would macros be too burdensome?

One of the benefits of macros is that they let your kwargs have arbitrary code, so you can use constructors, etc for them. For example:
#[kwargs(y: 5, z: MyStruct::new(4))]  // `new` can even use kwargs itself! In this case `y: 7`
fn myfunc(x: u32, y: u32, z: MyStruct) -> u32 { ... }

This is a much more ergonomic way to declare kwargs I would think, and I think it would probably be a mistake to allow this kind of behavior in non-macros.

The creation of variables you don't see is going to take up more bytes in the binary certainly

not more than adding code to it like let arg_val = match opt_arg { Some(v) => v, None = default };

you also can’t reference macros, and they’ll have no connection to “their” function other than the name.
sorry, i don’t think this is a good substitution at all.

@flying-sheep What do you mean "you can't reference macros"?

well, you can create references to functions

@flying-sheep Ah, I see what you mean.
This is actually a very good question -- would it be even possible to have zero-cost abstractions for function pointers/references with kwargs? Would all of the functions have to have the exact same kwargs (with the same default inputs!), or is it possible to have a per/function default.
I ask, because the problem seems non trivial to me. Say you have fn A(x: u8, y=18: u16) and fn B(x: u8, y=49: u16). If you have a function with function type fn(u8, u16), you simply wouldn't be able to use the kwarg functionality. How would the compiler know which value you want for u16 when you can take either A or B at runtime?
You might be able to write the function type to be fn(x: u8, y=18: u16), and then this would only be able to accept A but not B, and you wold be able to use kwarg functionality -- but this seems very limiting and not all that useful.
Without this restriction though, I don't think using kwargs with function pointers is even possible without incurring a cost (like carying around the default value with every kwarg function, forcing every function call to check if the function is a kwarg function to apply defaults, etc). Again, the macro makes it explicit that you don't care about the extra cost and you just want the convienience.

the macro option also has the benefit that it doesn't create two classes of functions, functions that have at least one kwarg implementation for them and functions that don't. There will always be a runtime penalty if a function could potentially take kwargs (i.e. if you had fn C(x: u8, y: u16) and then you defined A or B, using references to C would now be slower since the compiler would have to check whether there were kwargs).
A macro is clearly separate from a function, so it does not create this separation.

why would macros be too burdensome?

@vitiral Because Qt heavily uses optional args and function overload.  If we want to use macro, then you almost need to add ! to the end of every basic function.  More importantly, I think you cannot use macro in trait methods.

Actually it looks like you cannot even use macros as parts of crates or modules. I.e. I cannot write
use mylib;
mylib::!mymacro()

This is problem for my proposal (and actually a problem with macros, I'm curious to know why the devs chose this). I did not realize the full extent of this until now.
The fact that it is not easy to import and use macros seems like a problem with macros. I personally would like macros to be scoped like everything else, and don't see why that's not possible in theory.
Without scoped macros, though, obviously using macros for kwargs doesn't make sense

This is problem for my proposal (and actually a problem with macros, I'm curious to know why the devs chose this). I did not realize the full extent of this until now.

Because macro expansion happens early in the compilation process.

I would be interested in keyword-based / default parameters. One concern I have seen raised often is the fact that the parameter names become part of the API and thus changing it becomes a breaking change. Keyword-parameters should therefore be opt-in.
I am not sure about what designs have been proposed thus far, I skimmed through this issue and an old rfc but this is one idea I had:
// Normal function with positional parameters
pub fn positional(a: i32, b: i32) -> i32;

// Function with keyword parameters
pub fn keyword(pub a: i32, pub b: i32) -> i32;

// Function with default keyword parameters
pub fn default_keyword(pub a: i32 = 0, pub b: i32 = 2) -> i32;

// Mix of all
pub fn mixed(a: i32, pub b: i32, pub c: i32 = 0)

pub is already a keyword. Using it here would totally coincide with it's meaning: "making something public / part of the API". The use of pub would make it very clear that this argument is facing the outside world, making it part of the API and everything that that implies.
I thought I would write it down here so that others could share their opinions about this idea :)
Concerning the builder pattern, I think it is not a bad pattern. It definitely has it's use cases and they certainly do partly overlap with default / keyword parameters. But I don't think that should be a reason to exclude keyword parameters. When they fit on one line they are far less bulky than the builder pattern. In my opinion, lib authors could even propose both at the same time!

Wow @azerupi that's a fantastic idea for the syntax. I really like it.

@azerupi That's a solid suggestion!

@azerupi, nice idea. My only worry is that this syntax cannot be extended to a future RFC about  default values for struct fields.

Sorry, I am new here, so I can miss some nuances, but I really do not like such "pub" using. Yes, they can increase probability of breaking changes, but I doubt that parameter names will often change without changing their meaning in stable API. Besides, we have semantic versioning. And if you change behavior, then it will even be helpful to break compilation.
Additionally, it will be library authors' responsibility to choose between "stability" or convenience. I suppose library users can choose that for themselves. Moreover, library authors already can influence that by not creating functions with too many parameters, so users will not be tempted to use named arguments without reason. (:

Additionally, it will be library authors' responsibility to choose between "stability" or convenience. I suppose library users can choose that for themselves.

@DarkEld3r With enough users, somebody will end up relying on it. Then when you decide that a more expressive name would be nice, it becomes a breaking change.
If it is exposed, it becomes part of the API. Library authors bear the responsibility of tracking breaking changes and alerting users when they occur, so library authors should have the ability to limit what they expose.

If it is exposed, it becomes part of the API. Library authors bear the responsibility of tracking breaking changes and alerting users when they occur, so library authors should have the ability to limit what they expose.

I don't really understand what's the big fuss about exposing keyword argument names. The function name itself is already 'exposed' anyway, so changing it to a more expressive one would also break the API. Say, Python has had keyword/default arguments pretty much since the start and that's a great help for library developers rather than the opposite.
If you as a library developer would want to rename a keyword argument in a nice way, you could always support both the old and the new name for one release cycle and use #[deprecated] to trigger a compilation warning if an old name is used.

The function name itself is already 'exposed' anyway

And that was explicitly decided by using pub before the function. My understanding of @DarkEld3r's comment was that every pub function should have all of its parameter names be publicly available for use with keyword arguments. Fair point though; while this would add to the API a pub function exposes (you used scare quotes around exposed; is there a better word?), it's not a big deal to make sure that you're as confident in your parameter names as you are in the function name and (most importantly) the types when you make it pub.

Say, Python has had keyword/default arguments pretty much since the start and that's a great help for library developers rather than the opposite.

I feel like Rust has a somewhat different stance than Python. Rust makes some strong static guarantees, is often conservative in its defaults (e.g. immutability and privacy), and generally allows for a fair amount of control (e.g. pub struct vs. pub members1).

I suppose library users can choose [between "stability" or convenience] for themselves.

doesn't seem to fit with that. That said, I'm in no way opposed to either keyword or default arguments. I just want to make sure we're thinking things through.

If you as a library developer would want to rename a keyword argument in a nice way, you could always support both the old and the new name for one release cycle and use #[deprecated] to trigger a compilation warning if an old name is used.

How would that work?
1: This is different because it exposes more than just names.

Here are some thoughts on each of these ideas:
Keyword-Based Parameters
The biggest problem with keyword-based parameters is that they make it easier to write functions that take a large number of parameters. Ruby on Rails is a good example, with a number of functions taking 5+ parameters. I think most Rust developers would agree that this is an anti-pattern.
It also makes parameter names part of the public interface of every public function. While this may be less onerous, it is still a surprising change. It is also a major change of expectations for existing Rust code. Even if it's not considered a breaking change itself, it may be undesirable from the perspective of a current Rust library maintainer.
Variable-Arity Functions
Rust has macros, which can already be used to implement this sort of thing. Rust has a solid track record of keeping features orthogonal, and any non-macro-based system for this would be a strictly less-useful system re-implementing an ability already effectively available in the language.
Optional/Default Parameters
I don't have a strong opinion on this one. It seems like it's a new complication for very little gain, but maybe there are good arguments for it. All the ones I've seen so far in here have been aesthetic, which isn't a particularly compelling angle.

I don't think named parameters are an anti pattern, unless you consider the builder pattern one too, which is a very similar use case.  Having 5+ params in a function may just be bad design, or it may be needed.
Variable-Arity functions can be worked around by using vecs or similar, but still something that should be core of the language, rather than having to use macros for every new function you create.
Optional/Default Parameters are great at providing intent within the signature.
All these 3 features would be a great addition.

Sorry, I realize now the wording was ambiguous. I meant that having functions with a large number of parameters is an anti-pattern.
As far as variable argument function support, are you really needing them for every function?

Not every function, just ones where it makes sense and improves readability.
Yes you could use a macro, but each time you have a different function you would need to create a new macro to support it.

As I commented above, you really can't have anything but a macro because otherwise there is no way to have initial variables that are structs or made through (for example) a new() method. Having initial values is great, but kind of pointless without that ability (in my opinion).
The problem with macros is that they aren't well supported from a namespace standpoint, and so having a macro-per-kwarg-function is not acceptible.
Once macros are fixed to work inside namespaces though, I think that is the way to go here.

I've definitely wanted optional/default args. In most cases, it's "meh, good enough" to just have an extra function with a different name or allow None or Default::default() for the extra args, but in at least one case, I iterated over several solutions before deciding to take a single Into<AlgoRef> arg and implemented the trait for both &str and the tuple (&str, Into<Version>) so I can call algo("foo/bar/0.1") or algo(("foo/bar", Version::Minor(0,1))). It definitely feels hackish/unnatural to need the extra parentheses to accomplish this, but for the particular requirements, I preferred this trade-off (and have followed this issue ever since).
As for variable-arity, I haven't actually encountered a need, though I do think it'd be elegant if method-level macros existed to call things like logger.info!("foo: {}", foo) - but I won't pretend to understand the implications.

It's probably easiest to leave variable-arity to macros, well they are used quite rarely.
I wonder if named arguments could be achieved with very ergonomic anonymous structs?  You might call it like f({ .. }) because it's anonymous.  I donno if named arguments are wise, but the extra { .. } might help discurage their abuse.
I think care should be taken with default arguments since they could be used by mistake.  A priori, I'd suggest the programmer to writes a placeholder like f(a, , b) or f(a, !, b)..
Another option is simplify giving default parameters a specific name, so maybe an enum but probably a macro or inlined, and maybe scoped to the function.  It offers the advantage that multiple defaults can be specified, but abusing defaults is discouraged.

It's absolutely doesn't matter what is anti-pattern and what is not.
Programming language is a tool, not a vice squad. Yes, I don't like
functions with many arguments, but sometimes we need it. I also don't like
"unwrap", but sometimes it's needed. If something is just case of taste -
it's not an argument.
On Fri, 25 Mar 2016 at 14:29 Jeff Burdges<EMAIL_ADDRESS>wrote:

It's probably easiest to leave variable-arity to macros, well they are
used quite rarely.
I wonder if named arguments could be achieved with very ergonomic
anonymous structs? You might call it like f({ .. }) because it's
anonymous. I donno if named arguments are wise, but the extra { .. }
might help discurage their abuse.
I think care should be taken with default arguments since they could be
used by mistake. A priori, I'd suggest the programmer to writes a
placeholder like f(a, , b) or f(a, !, b)..
Another option is simplify giving default parameters a specific name, so
maybe an enum but probably a macro or inlined, and maybe scoped to the
function. It offers the advantage that multiple defaults can be specified,
but abusing defaults is discouraged.
—
You are receiving this because you were mentioned.
Reply to this email directly or view it on GitHub
https://github.com/rust-lang/rfcs/issues/323#issuecomment-201245439

Variable Arity and named parameters are also great predecessors to more FP stuff like currying.

Let's all be friends
It sounds like everyone is opposed to functions that do too much, and by extension may have way too many args, but I don't think that keyword/default args or variable arity should be on the chopping block because they could be misused.
Addressing concerns against this RFC
Many argued strongly in favour of using structs, and I'm using structs as args extensively in a library I'm working on; but, that's because the data should be grouped together like position: Xyz{x: f64, y: f64, z: f64}. However, this is a far cry from being a perfect solution in all cases.
To address @AndrewBrinker,

Variable-Arity Functions
Rust has macros, which can already be used to implement this sort of thing. Rust has a solid track record of keeping features orthogonal, and any non-macro-based system for this would be a strictly less-useful system re-implementing an ability already effectively available in the language.

I am strongly in favour of re-using existing tools if possible, but only if they're a good match for the job. I'm not even sure if macro's could solve the following problem, regardless their documentation doesn't make them look trivial to use/understand/maintain (and I'm no stranger to meta-programming and manipulating an AST for codegen). Whereas in the following code with variable-arity functions allowed, it's trivial for even a novice to understand what's happening.
Example For Variable Arity
After doing a bunch of programming in Erlang/Elixir, I really miss the ergonomics of variable arity/default arg functions at times like below:
pub struct SceneGraph<'a> {
  pub tree: RefCell<RoseTree<GraphNode<'a>>>,
  root: GraphNode<'a>,
}

impl<'a> SceneGraph<'a> {
    pub fn add_node(&mut self, parent: NodeIndex, node: GraphNode<'a>) -> NodeIndex {
        ......//Extra logic removed
        self.tree.borrow_mut().add_node(parent, node)
    }
    pub fn add_node(&mut self, node: GraphNode<'a>) -> NodeIndex {
        self.add_node(self.root, node)
    }
}

Alternative Implementations
A: add_node/3, where node: Option<GraphNode<'a>>
B: rename add_node/2 to add_node_root/2.
If I understand correctly, A would carry a runtime penalty to unwrap and test.
B entails an additional method name/ergonomic issues to deal with when using the library. Alternative B also embeds unnecessary implementation details in its public interface, this poses a challenge if an internal implementation detail changes**, and then the method no longer accurately reflects its purpose, prompting me to rename it to add_node_wubba_lubba_dub_dub/2. For the sake of this discussion, please don't get pedantic about this example, and focus on the spirit of the use-case (It can be useful for multiple functions to share the same name, because externally they do the same thing, and your public interface shouldn't leak implementation details.)
**With no change to the end user because :heart: semver.
Suggestion
If this gets supported, +1 for @azerupi's syntax suggestion, repeated below (although it might need some refinement to also work with variable-arity; Erlang/Elixir are good examples of doing both variable arity and default args).
Keyword args/defaults should be opt-in on a per variable basis, for the reasons @reddraggone9 mentioned.

// Mix of all
pub fn mixed(a: i32, pub b: i32, pub c: i32 = 0)
// Variable Arity
pub fn mixed(a: i32 = 0)

Would create functions mixed/0, mixed/1, mixed/2, mixed/3. Where mixed/0,1 invoke the latter definition, while mixed/2,3 invoke the former. These need to be mutually exclusive sets, unless we want ambiguous function choices from the compiler a la Java et al, which IMO should be a compiler error.
Note: NAME/0 - Denotes  a function called NAME with arity 0 (Takes 0 arguments).

Optional/Default Parameters
Can't a compiler just to reorder keyword arguments and fill an omitted arguments places with a default values (or raise an error when an argument does not have a default value and is not specified)? Consider the following function:
fn foo(a: i32, b: i32, c: i32 = 3, d: i32 = 4)

A programmer may call it using:

all positional arguments: foo(1, 2, 24, 71) - just like it works right now
positional arguments and (optionally) keyword arguments:

foo(5, 6, c: 17) - gets translated into foo(5, 6, 17, 4)
foo(5, 6, d: 48) - gets translated into foo(5, 6, 3, 48)
foo(5, 6) - gets translated into foo(5, 6, 3, 4)

positional arguments with keywords syntax:

foo(b: 35, a: 18) - gets translated into foo(18, 35), which, in turn, gets translated into foo(18, 35, 3, 4)
foo(b: 35) - throws a compile error, because a is not specified and does not have a default value

I believe, this will not have an impact on a runtime performance, because arguments reordering and stuff will be performed during a compilation.

@zmoshansky How do you propose handling a case like this:
pub fn add_node(&mut self, node=GraphNode.origin(): GraphNode<'a>) -> NodeIndex {
        self.add_node(self.root, node)
    }

Clearly (to me at least) you cannot do this in anything but a macro -- you are calling a method in the declaration.

@vitiral, Thanks for the feedback. As I understand it (GraphNode.origin() being GraphNode::origin()), this would be easiest to do with variable arity functions. By defining a add_node/1 this could be easily achieved as follows. Essentially add_node/1 would call on add_node/2 which would call upon add_node/3.
pub fn add_node(&mut self) -> NodeIndex {
    self.add_node(GraphNode::origin())
}

@vitiral You can just restrict the default values to whatever is assignable to const, including const fn, problem solved.

@e-oz, encouraging antipatterns by providing fancy syntax for it is not something that is aligned with Rust's design philosophy.

@zmoshansky ah, I see. I see now that variable arity was one of the options given in the thread title -  I was pretty focused on kwargs.
Variable arity is certainly possible. I'm honestly not sure why it's not implemented already since it so simple. I could see it causing problems with function documentation (since you now have multiple functions), as well as lending a hand to making confusing APIs. It might help in some places though.

@aldanor I would see that as of pretty low usefulness, but maybe that is just me. Having complex structs with new defined, but not actually being able to pass any variables in would get annoying. You would have to reimplement new for every value.

Coming from python it seemed at first glance like this would be really useful for dynamic string formatting (#642)
let template = String::from(".. {id} .. {id} .. {id} ..");
template.format(id="foo");

... but that won't actually work. In python keywords are just hashmaps which is a nonstarter for rust.
Regarding keywords this changes my previously tacit :+1: to :-1:, unless I myunderstood and keywords aren't supposed to be just syntactic compile time checked sugar for positional arguments.

Coming from Python, too, I'd say I prefer using keyword-only arguments whenever possible because it reduces ambiguity and prevents from shooting yourself in the foot by accidentally mixing up the positional args and keyword args.
With this in mind, maybe this could work:

the first keyword argument must follow the last positional argument
keyword arguments must always have a default value
default values must have static lifetimes (string literals should be fine)
this implies that keyword arguments cannot be &mut
at the call site, keyword arguments may only be passed by keyword
at the call site, positional arguments may only be passed by position

Example:
fn foo(x: u32, y: u32 = 0, z: u32) -> { () } // ~ERROR positional arg after a keyword arg
fn bar(x: u32, y: u32 = 0) -> { () }         // ok

fn main() {
    bar(1, 2);     // ~ERROR `y` is a keyword argument
    bar(x=1, y=2); // ~ERROR `x` is a positional argument
    bar(1, y=2);   // ok
}

While this looks quite simple, I believe it would be sufficient to cover the cases that currently require using the builder pattern.

@aldanor Could you please explain the rationale behind your proposed restrictions? I worry that it sacrifices a lot of the flexibility of this proposed feature, for an unknown benefit.
I do like the idea that positional args must be defined before keyword arguments; Although, it's not a technical requirement AFAIK.
I strongly oppose that keyword arguments must have a default value, and to use a default value, you must use a keyword argument. Especially given the call site requirements you've outlined.

As an aside, I've opened a new RFC #1586, to deal with variable arity functions as this thread seems focused primarily on kwargs & defaults.

@zmoshansky The rationale is that I think this is a less intrusive approach, easier to implement, and it's possible to later extend it if that's ever needed. I believe this fully addresses the pain of having to write Builders in cases where multiple optional parameters (that always have defaults) are needed.

I strongly oppose that keyword arguments must have a default value, and to use a default value, you must use a keyword argument.

Could you provide concrete examples where any of the proposed restrictions would impose a functional restriction on your code? (syntactic sugar aside)

@aldanor, Thanks for that thoughtful reply. I agree that this provides a nice alternative to the builder pattern, which I'm not a fan of being forced to use (Time and a Place for everything). My feedback was directed at the broader use of these features, outlined with reasoning below.
I don't believe that coupling these two features would impose any functional restrictions, although I feel that the entirety of this RFC is really about non-functional syntactic sugar. When I say that I mean that everything could be implemented with zero runtime overhead in a different manner (although potentially quite ugly).
To clarify, I strongly oppose the tight coupling of keyword and default arguments, where one is impossible without the other. In my personal opinion, I feel that this tight coupling of related features violates Single Responsibility and Low Cohesion principles and would actually make the implementation less robust. Even if the usage of keyword arguments subsumes default arguments to be practical, I think that it's a fallacy to assume the default arguments necessitate keyword arguments.
I feel that they both have great merits, and for instance, I would love to use default arguments with positional arguments. This may be a moot point if the envisioned call syntax using a default arg is as follows bar(1); (where y=0).

I started playing around with some options last night. Partially inspired from Racket, and ML. I think something like this could work well. The main idea is that there is a correspondence between our current function definition syntax and tuples, so named arguments should correspond to struct syntax. Then the task of adding in default arguments because a bit simpler. The big concern with this idea is that it's too big of an addition to the existing syntax, and if it's even backward compatible at all. I haven't thought about that enough yet.
Here's some examples.
// 1. Tuple style method call (current functions).
fn foo(a: u32) {}
foo(2);

// 2. Struct style method call.
fn foo { a: u32 }
foo { a: 2 };
// SomeType { a: 1 } -> SomeType
// SomeFunc { a: 1 } -> Codomain
// generally_lowercase { a: 1 } -> Codomain

// 3. Together.
fn foo(a: u32) { a: u32 } {}
foo(1) { a: 2 };

This syntax for function decs/calls is consistent, and should work well with both optional and default arguments.
// 1.
fn foo(a: u32 = 1) {}  // Using const function.
fn foo(a: u32) {}      // Using `Default` trait.
foo();

// 2.
fn foo { a: u32 = 1 } {}  // Using const function.
fn foo { a: u32 } {}      // Using const function and `Default`.
foo {};

// 3.
fn foo(a: u32 = 1) { b: u32 = 1 } {}  // Using const function.
fn foo(a: u32) { b: u32 } {}          // Using const function and `Default`.
foo();
foo {};
foo(){};  // We probably want this for consistency (macros).

In practice I can see the named argument syntax getting very long, and people wanting to do things like this:
fn foo(...) {
    arg1: u32,
    arg2: u32,
} {
    ...
}

This is obviously not good. We could then alternativly use an #[arg(...)] or #[named_arg(...)] attribute. This depends on https://github.com/rust-lang/rust/issues/15701 for function call attributes.
There would be a decision of whether or not we want to allow all three types of args in attributes or just named arguments. Below are some examples with all three.
// Declaration.
#[arg(a: u32)];
#[named_arg(b: u32)];
#[ret(u32)]
fn foo {
    unimplemented!()
}
// ==
fn foo(a: u32) { b: u32 } -> u32 {
    unimplemented!()
}

// Call.
#[arg(a = 1)];
#[named_arg(b = 2)];
foo();
// ==
#[arg(a = 1)];
#[named_arg(b = 2)];
foo {};
// ==
#[arg(a = 1)];
#[named_arg(b = 2)];
foo(){};
// ==
foo(1) { b: 2 };

// Example with defaults.
/////////////////////////

impl Channel {
    #[arg(timeout: Duration = Duration::from_millis(10))];
    #[arg(pre: Option<Fn(D) -> bool>)];
    #[arg(post: Option<Fn(C) -> bool>)];
    fn call<D, C>(domain: D) -> C {
        unimplemented!()
    }
}

#[arg(timeout = Duration::from_millis(20))]
channel.call(message);

#[arg(timeout = Duration::from_millis(20))]
#[arg(pre = |d| d > 10)]
#[arg(post = |c| !c.is_empty())]
channel.call(message);

I have some concerns with using Default implicitly, as it could make it very easy to forget an argument and then have the program do something. One of the best things about rust is how I can use the type signatures to drive my development.
Last we could further alleviate issues describing function signatures with a new sig keyword. I'm not necessarily recommending this, mostly just exploring options.
// Signature declaration.
sig foo(a: u32) -> u32 {
    b: u32,
}

// Function declaration.
fun foo {
    unimplemented!()
}

// Call.
foo(1) { b: 2 };

That's the best proposal I've seen so far, @nixpulvis. I agree that the use of Default is worrisome, since having function signatures be explicit is such a nice benefit for understanding code, especially code you may not already be familiar with. I don't like the use of attributes for anything related to these features, however. Attributes make more sense to me as a special instruction to the compiler about the item. If there are other non-signature related attributes, it may be quite difficult to read and understand exactly what the signature is. If the signature (sans-attributes) being verbose is something that people find unappealing, perhaps that would be a deterrent for having a signature with a very large number of options and giving one function too much responsibility.

I used to think that optional/default values would have to be constants (or immutable statics) to be feasible.
However, https://github.com/rust-lang/rfcs/pull/1587, even if it got closed, provides another interesting way to provide default values.
My main critique of https://github.com/rust-lang/rfcs/pull/1587 was that it used to allow arbitrary overloading by arity, which I found confusing. (I generally don't like ad-hoc-overloading, it feels hacky and unsystematic.) But with some tweaks (some of which were provided by the last commit in the RFC), the ad-hocness disappears.
Simply put, there is a "full" form of our n-ary function that contains all the n parameters, for example fn example(a: i32, b: i64, c: u8), but you can also define "shortened forms": fn example(a: i32, b: i64) and/or fn example(a: i32) and/or fn example(). The forms form a set, so that the names are the same but arities are different and mutually exclusive, and the types and parameter names in the common prefixes are always the same, like in https://github.com/rust-lang/rfcs/pull/1587.
What would be different from https://github.com/rust-lang/rfcs/pull/1587 is that the shortened forms are required to tail-call some other form that has larger arity than the caller. This ensures that the body of the full form is always called, and the shortened forms then function basically as default-value set-upper shims. This has the benefit that the default values don't have to be constants. (For example, in a case where the default value can be inferred from some other parameter in case no value is provided by caller.)
If the default values were bound to be constants in cases where constants simply don't cut it, you'd have to use Option<T> and set None as the default value, which requires unwrapping and setting up the default value inside the function, which is kinda icky. I'd imagine a shim-style set-upper would generate more efficient code too.
This is just an idea, it would still require thinking about the fully qualified paths, and the interactions with kwargs (with a default-valued kwargs, the shims would not form a total order but some kind of a lattice), plus revive the discussion about tail-calling.

Ah, one more thing, about default values using the Default trait: I find this to be problematic, because default() builds the whole struct, even if just a single default value of many is being needed. Building the default values might have side-effects so it might be hard to optimize away the construction and dropping of unused default values.

I think this topic is really important because it impacts significantly Rust libraries public API, forcing developers to use some workaround that make their API less clean and less elegant.
While I understand why the 3 proposals has been grouped together, my concern is that there is too much here to make significant progress and move forward. So my proposal would be to focus on improving Rust current capabilities instead of adding multiple alternatives for doing the same thing that would be confusing for the developer, make the compiler more complex/slow and impact runtime performances.
So why not focusing on improving position-based parameter rather than introducing something new that could be confusing for users (should I use position-based or keyword-based parameters ...)?
For position-based parameters, I think the optional parameters proposal is great because simple, it is an improvement in line with current Rust mindset, and would be VERY useful to design clean APIs. I don't think variable-arity would add much more benefits.
So in practice, reusing @orgkhnargh example, for we could only define on foo function, but have the capability to specify optional parameters with default constant values:
fn foo(a: i32, b: i32, c: i32 = 3, d: i32 = 4)
A programmer may call it using:

foo(1, 2)
foo(1, 2, 24)
foo(1, 2, 24, 71)

That's all.
Such change would be useful for all kind of applications IMO, and would especially allow more wider adoption for Rust for developing high performance Rust web applications. That could also help making Rust a good alternative to Go or Swift 3 for server-side applications (when high-performance is needed, Reactive applications could also be a good use case).
So I hope a RFC focusing on optional parameters with default values will be created/reopened with higher priority, and this one updated to keep discussing with a postponed status only about keyword-based parameters and variable-arity.  Rust developers feedback after optional parameters adoption could provide great insight to help deciding what to do on those 2 remaining proposals.

My problem with many of the proposals here, is that they encourage an anti-pattern, namely having too many arguments, where grouping it into a simple structure would be considered better pratice. In many of the cases made in this thread, either builder pattern or a struct would be sufficient.

I agree that with named-parameters there is a big risk for too many arguments, but I think the risk is much lower with optional parameters. Optional parameters with default values allow you, as an API designer, to give the possibility to the user to provide less parameter if he wants to rely on the defaults your have defined while allowing to make your API self-documented about these default values (and documentation can obviously reuse that automatically). But unlike named-parameter, you have more constraints due to the order of arguments, since if specify an optional parameters the previous ones must be specified.
What I have seen in languages supporting such feature is that this is widely used, usually in a good way. That seems to me a good trade off.

My problem with many of the proposals here, is that they encourage an anti-pattern, namely having too many arguments, where grouping it into a simple structure would be considered better pratice. In many of the cases made in this thread, either builder pattern or a struct would be sufficient.

... that is, unless you consider the builder pattern to be an antipattern on its own. Being forced to create an alien builder struct just so you can have a ctor that takes 1, 2 or 3 arguments is more of an antipattern in my opinion than having a ctor with two optional arguments with defaults. Sure, if you have more than one function expecting this same of arguments, it calls for the builder pattern, but that doesn't happen too often, not even in the standard library.
Another point which may have not been raise here is that the generated docs may end up being easier to read if the default/optional args are used instead of builders -- e.g., it's a lot nicer to be able to see the full signature of a ctor on the relevant struct's page where it belongs as opposed to having to read the separate page for the associated builder.

I agree with @aldanor. To me the builder pattern feels worse than keyword arguments. Same for exporting an option struct.
Let's take an extreme example using python: http://pandas.pydata.org/pandas-docs/stable/generated/pandas.read_csv.html
For some people that would be a very bad function, 45 args!  You could try breaking it into function that takes at most 2-3 arguments but good luck with that.
Builder pattern is out, not going to write 44 methods and it just feels wrong (more on that later). Option struct and kwargs are left.
Let's take a simple CSV reading example:
df = pandas.read_csv('data.csv', encoding='latin1', low_memory=False)

Here are the options in Rust:
// option struct
let options = CSVOptions {
  filename: "data.csv",
  encoding: "latin1", // could be an enum
  low_memory: false,
};
let df = pandas::read_csv(options);

// kwargs
let df = pandas::read_csv('data.csv', encoding='latin1', low_memory=false);

The kwargs option is in my opinion much clearer because the library doesn't have to export a specific struct for each function which would be a horrible solution. In my opinion kwargs make libraries API much nicer and is something that I seriously miss in Rust.
Let's take a Rust example from glium:
// builder pattern
let display = WindowBuilder::new()
        .with_dimensions(1024, 768)
        .with_title(format!("Hello world"))
        .build_glium()
        .unwrap();

// kwargs
let display = WindowBuilder::new(
     dimensions=(1024, 768),
     title=format!("Hello world"),
).unwrap();

I have a hard time understanding why people would prefer the first one other than being able to conditionally change some bits of the builder, which can be done in Python as kwargs is just a dict. If anyone preferring the builder pattern can chime in, I would appreciate it!
I personally love @azerupi proposal but wouldn't it be better to separate the kwargs/default args (I feel like you need both to do nice APIs) and variable arity into two different issues?

Not sure if this has been suggested before, but you could hang the function call off the options struct itself directly:
pandas::ReadCSV { "filename: data.csv", encoding: "latin1", low_memory: false }.call()

If https://github.com/rust-lang/rust/issues/29625 is ever stabilized it could also be:
pandas::ReadCSV { "filename: data.csv", encoding: "latin1", low_memory: false }()

@Keats for most people it isn't really about whether we like kwargs, it's the technical challenge of fitting it into rust without it causing overhead.
@glabhoerl that would be fine for some use cases, but doesn't address default args (which is 95% of the point of kwargs IMO).
If you had default struct args you could be part of the way there, but that still doesn't address default non-static types. I honestly don't know if that can ever be addressed. Some possibilities:

start off only supporting immutable static variables
have roadmap for the stdlib to support lazy_static! variables so that kwargs can use them as well
don't open feature gate until everything is supported

Maybe lazy_static! already does what you need it to do and all of this could be done. That would certainly be nice.
I would like to still point out that you would have to completely throw away the kwargs when you are using the functions as a function pointer, as holding that data would drastically increase memory and performance costs.

One of the reasons I like @glaebhoerl 's suggestion (combined with mine) is that it solves kwargs in two spheres: Struct and Fn, but would not add additional types of Fns.

For keyword arguments, @flying-sheep's trait + sugar based proposal seems ideal to me (https://github.com/rust-lang/rfcs/issues/323#issuecomment-1651847030), the only issue being @vitiral's comment about the need to support what could essentially be described as runtime in addition to compile time default argument value instantiation (https://github.com/rust-lang/rfcs/issues/323#issuecomment-178053176).
I just wanted to point out that this doesn't really seem like it should actually be a problem -- wouldn't you just have the default value be None, and then manually instantiate the actual default value in the function body?  (In which case, maybe the desugaring should convert value to Some(value) for any argument struct member with Option type?)

working link for my proposal

After reading the whole conversation I clearly see that all three issues here should be discussed/implemented separately. Especially It's the case for variable airity. It already has been stated a few times here, but clearly those issues are still mixed up here and there.
From my point of view keyword syntax for argument passing and default values are good things to have (less boilerplate, cleaner look, better readability).
Personally I'd prefer @flying-sheep's proposal, but the one from @azerupi looks good for me too.

I have started a pre-RFC discussion on the internals forum about named parameters: https://internals.rust-lang.org/t/pre-rfc-named-arguments/3831

you forgot to mention my proposal under the alternatives. is this intentional?

Not intentional :confused:
I definitely should mention it, as it is quite a good alternative. I will update the proposal. Thanks for remembering me :)

thanks!

I'm referencing #1806 , as I believe it has a potential impact on the future of these features.

Forgive any ignorance about the builder pattern as I'm new to the language, but how does it help if you're just trying to write a trait and you want some of the function signatures to have optional and/or keyword arguments?

Would really like to have this feature as well. Am new to Rust, and I'm surprised it doesn't have such a convenience feature.

They just released the 2018 roadmap with no mention of default argument values or named arguments https://blog.rust-lang.org/2018/03/12/roadmap.html 😢

Maybe add function overload over arity?
fn foo(a: u32) -> u32 {
    a + 5
}

fn foo(a: u32, b: u32) -> u32 {
    a + b
}

fn main() {
    let _1 = foo(1); // 6
    let _2 = foo(1, 3); // 4
}

@sergeysova I predict that different arity will instead happen via variadic generics and traits.
Using C++-like syntax as a placeholder, that could mean something like
trait Foo<T...> {  fn foo(args...: T...) -> Self; }
impl Foo<u32> for u32 { fn foo(a: u32) -> u32 { a + 5 } }
impl Foo<u32, u32> for u32 { fn foo(a: u32, b: u32) -> u32 { a + b } }
fn foo<T..., R>(args...: T...) -> R { <R as Foo<T...>>::foo(args...) }

(aka basically the same pattern by which "overloaded" things like Vec::get work.)

Over in #2443, @Ixrec has asked me why I feel critical about named/optional arguments. @steveklabnik suggested I reply here so that's what I do.
There are a few reasons:

optional arguments hide the fact that you can pass more arguments to the function. If you type None this is visible. Right now when I want to pass some arguments to a function, I know that I either have to change the function and all of its invocations, or I have to look for alternatives. With optional arguments, I'd have to look at the function declaration.
I feel that the builder pattern solves the issue well enough for constructors. Especially, functions with too many parameters feel bad to me.
I am afraid of wide breakage ensuing if named args are adopted... will we rearchitect huge codebases to use named arguments? will we go the way of swift and break the standard library for this purpose?
There is a readability benefit of named arguments. system.open_valve(id, 1.00, false) is pretty cryptic while system.open_valve(id, opening_speed=1.00, emit_event_when_open=false) reads much better. However, I feel that this readability increase is better done by IDE tooling (like here: https://youtu.be/ZfYOddEmaRw).
I feel that named args/optional params/etc introduce a new way of doing things even while the existing way is well enough. So I'd probably want a lint for my code to not declare functions/traits with named args so that my code uses less language features and is thus easier to understand by people who learn the language as they encounter new features. So basically named args are not worth the price in additional language complexity and harder learnability.
I think it is not really avoidable for API stability reasons that the way a function is declared forces you how to invoke it. Right now you may change the name of parameters without a semver breaking change and I presume that there are already crates on crates.io which have done this between minor semver versions. This choice, that function declarations forcing how you invoke them has the potential for fracturing of the ecosystem. Some will write libraries that are "named arg" version of X and others will write "positional arg" version of Y crates...

Macros which expand to function calls have to worry about keyword arguments. Potentially need new macro machinery to both accept keyword arguments, and also to "match" keyword arguments.

sad news...

Re point 3, this is already happening with async IO and futures, no need to worry about something that already exists. In fact, I'd argue named arguments (as an optional feature) is more compatible than trying to get futures to work with other libraries.
Re point 4, I'm glad your IDE works well, but that is not the case for all editors people use with rust, and implying that the support there is good enough to not improve the language legibility simply sounds like you lucked out on which editor you prefer.
Re point 6, is there anything actually stopping people from breaking semver in crates?

You need to write a lot of additional boilerplate code for builder pattern and it only usable when ALL arguments are optional, because you can't force API user to call builder methods. It's not always the case, obviously, often functions have required arguments.

Well, it's enough just to check history of this issue to lose any hope about this feature. Rust is not good enough to get this feature.

Re point 3, this is already happening with async IO and futures

Sunken cost fallacy. Also, with async IO there is a strong technical reason for doing it, it's not done for fun :).

Re point 4, I'm glad your IDE works well, but that is not the case for all editors people use with rust

Definitely a legitimate counter argument. But I still feel that the desire whether you want to see argument names or not is subjective and depends on the situation and thus is managed flexibly by a tool and not put into persistent source code.

@Pauan it's not part of the builder. With same success you can call setters "a pattern builder".

The builder pattern doesn't really work when you are dealing with simple functions, unless you want to export a struct for each function.
I use PyCharm so I do get the nice tooling you mention but I don't have it when doing reviews on GitHub or elsewhere.
This argument

With optional arguments, I'd have to look at the function declaration.

is equally valid for the current state, you have no way of knowing what the parameters of system.open_valve(id, 1.00, false) mean without looking at the declaration.
I can totally see the reasons for not wanting named args/kwargs in the language but that would be the biggest ergonomics win for Rust in my eyes.
Regarding 3, if the approach to implement follows Python, there shouldn't be any breakage no? Named arguments are opt-in at the call site in Python so you don't have to use them if you don't want to.

Regarding 3, if the approach to implement follows Python, there shouldn't be any breakage no? Named arguments are opt-in at the call site in Python so you don't have to use them if you don't want to.

The point is that if a function renames one of its arguments, that will break anything that is still calling it with the old argument name.

The point is that if a function renames one of its arguments, that will break anything that is still calling it with the old argument name.

Just like any function? If you rename an argument, you will likely rename the builder fn as well in the case of a builder which would be a breaking change as well, unless you keep the old one around. I view the argument names being part of the type as a plus as an argument rename might mean a behaviour change that could go unnoticed otherwise

@Keats It's not about builders: if named arguments were added it would affect all functions.
For example, during the prototyping stage you might have a function with an argument called a, and then you want to rename it to a better name like price.
Or if you change the function's internal implementation then you would want to adjust the parameter names to match.
As @est31 said, people currently take advantage of that to rename function arguments in non-breaking ways, but they can't do that if named arguments were added.
You view them as being a part of the type because that's how it is in some other languages (like Python), but that's not how it is in Rust. So it would be a big change to the entire ecosystem.
There might be ways to avoid that particular problem: for example by requiring a #[keyword_arguments] attribute, so that way if you don't use that attribute then you can rename the function arguments in a non-breaking way.
To be clear, I'm not against named arguments, I'm just clarifying facts so that the decision can be made with the most information.

@Pauan @Ixrec
Multiple variations of opt-in named args have been mentioned both in this thread and in a related internals discussion linked above. In fact, it looks like making them opt-in was the plan. I would recommend that anyone thinking about contributing here read both threads. Otherwise we'll just keep retreading old arguments, which doesn't add much to the discussion.

@varkor yes the IDE argument is very flexible and applicable in multiple directions. You could e.g. argue that let should be replaced by a 100 letter keyword and if someone has problems with that, they should change their IDE to make it appear as let again... But here there is an existing IDE feature and I think it gives us two benefits:
a) you can configure it to display names whenever you want for your use case... I call this IDE induced foveation
b) crate authors can freely change argument names

optional arguments hide the fact that you can pass more arguments to the function.

This is also true of the builder pattern.
Though I do share your concern about forgetting to specify an optional argument. Perhaps rust should have a syntax like _ in match to denote that you're opting into the remaining defaults.

I feel that named args/optional params/etc introduce a new way of doing things even while the existing way is well enough.

Rust already has two ways to approach this pattern Default + .. and the builder pattern. I'd argue that neither is sufficient and present barriers to entry.
Default is simple enough but has well known limitations (all or nothing).
The builder pattern feels odd and clunky if you come from a background of languages with named parameters. Even if you've seen it before the Rust builder pattern has nuances that don't exist in other languages which hurt its learnability: e.g. which flavor of self should be used in the finalizer?,  should it impl Default, would a simple closure be better, etc.?

One way to avoid the chaos named arguments can bring is to only allow named arguments appear on private functions (that is, functions that are not marked as pub and not a trait method). This way, the feature will still be pretty neat, and we can evolve on this path.

Another possible option would be to both introduce language syntax for
named arguments to distinguish them from positional ones (@name?) and
require that the names be used at call sites.

is to only allow named arguments appear on private functions

That doesn't solve my complaint with non-opt-in named arguments: that it changes improving a name from a local question (since it can only affect the current function) to a module-level question.  That's annoying for both humans and IDEs, and as a result discourages improving names.

optional arguments hide the fact that you can pass more arguments to the function.

Or they hide the fact that you can pass fewer arguments at the function call side.
But don't call any function if you don't know its signature and its intention.
I don't know why there is so much hate about optional args and named arguments, they are both syntactic sugar which is resolved by the compiler.
I don't know why this should be a breaking change since functions with default arguments are normal functions with the same arity as before but with the option to omit some arguments at the call side in favor to default settings suggested by the library/function creator. The omitted arguments are then filled up by the compiler.
Whereas named arguments don't change the arity of the function only the struct in which all named args are packed in. Named arguments are better than normal struct parameters in that they allow default values which don't have to be defined in a struct outside the function and which can be easily overdefined without to create a second default struct where only one value of 40 values has changed.
And no, keyword arguments are also type checked.
Optional and named arguments will significantly reduce the misuse of traits in order to achieve variability or optionality.
Variadic arguments are more powerful than arrays in that they can allow for heterogeneous types. Further variadics are some form of generics which allow to parametrize over function signatures.

@leodasvacas yeah foo(arg,..) would fix my point if it were mandatory. Then it'd be the same tradeoff like for struct initializer syntax.

@leodasvacas adding to that: there is surely a tradeoff here of features. I don't think that there is a positive sum game result here, sadly :/.
Another point:

Permutating order means that checking two calls for equivalence turns from a O(n) problem to a O(n^2) problem.
Easy to check that they are the same:

system.open_valve(id, 1.00, false, true)
system.open_valve(id, 1.00, false, true)

Hard to check:
system.open_valve(id, emit_event_when_open=false, send_drone=true, opening_speed=1.00)
system.open_valve(id, opening_speed=1.00, emit_event_when_open=false, send_drone=true)

Even harder to check (you'd have to check the signature of the function to find out the default for emit_event_when_open):
system.open_valve(id, emit_event_when_open=false, send_drone=true, opening_speed=1.00)
system.open_valve(id, opening_speed=1.00 send_drone=true)

The last example can be fixed by a lint that checks whether you set a named optional arg to the default.

@est31 I'm sure rustfmt would be able to order your named arguments alphabetically.

You could also just do an O(n) walk over the original function definition to see which arguments are used at each callsite, I don't see why doing an O(n^2) blind comparison between two call sites is ever necessary:
def: system.open_valve(id, opening_speed=1.00, emit_event_when_open=false, send_drone=true, opening_speed=1.00)

ex1: system.open_valve(id, __________________, emit_event_when_open=false, send_drone=true, opening_speed=1.00)
ex2: system.open_valve(id, opening_speed=1.00, emit_event_when_open=false, send_drone=true, __________________)

Lol when I mentioned landau O I've originally meant how long it takes for me to compare the lists manually. But yeah if they are sorted such a comparison can be done in O(n). I don't have O(n) memory though nor do I want to compute hashes in my head so the unsorted case is still in O(n^2).

This is not really ever an issue in other languages that have keyword args though, e.g. Python.

long it takes for me to compare the lists manually.

I'd like to point out that this is also a problem in structs where there is no implied ordering of fields, and with the .. syntax you may not even have all of the fields present. Builders also have similar issues, but theirs less intent expressed to the compiler, so tooling like rustup can't reorder the optional "params" in a way that's easy to compare. Worse yet with a builder the order of the calls may effect the semantics of the program!

foo(arg,..) would get rid of the main purpose of the default arguments -- to make it so that you can extend an API by adding new arguments to a function (that previously had all mandatory arguments) without breaking existing callers. The common case for this is that you realize there is some additional aspect of the function's behavior that should be customizable, but where most of the time there is a sensible default (which is usually the original behavior prior to adding the new argument). If you didn't have the foresight to use the builder pattern or whatever special macro people are imagining, right now you're out of luck.

Since we are brainstorming, I would like to pitch in my thoughts on this. The syntax inspiration is from Dart as well as Swift.
The arguments can be both positional and named & it is backwards compatible.
fn function(a: bool, { b: i32 });
=> function(false, b: 54);

fn function({ a: bool, b: i32 });
=> function(a: false, b: 54);

/// Not allowed.
fn function({ a: bool }, b: i32);

/// Current syntax still works
fn function(a: bool, b: i32);
=> function(false, 32);

/// Swift like external names for named arguments
fn start({ from start_loc: u32, to end_loc: u32 });
=> start(from: 4, to: 10);

With Default Arguments
fn function({ a: bool = false, b: i32 = 4 });
=> function();
=> function(a: true);
=> function(b: 6);

fn function(a: bool = false);
=> function();
=> function(true);

/// All allowed syntax
fn function({ a: bool, b: i32 = 4 });
fn function({ a: bool = false, b: i32 });
fn function(a: bool = false, b: i32 = 4);
fn function(a: bool, b: i32 = 4);
fn function(b: i32, a: bool = false);
fn function(a: bool, b: i32 = 4, { c: bool });
=> function(true, 5, c: false);
=> function(true, c: false);
fn function(a: bool, b: i32 = 4, { c: bool = true });

The order of the argument names:

Non-optional positional args.
Optional positional args.
Any keyword args.

Wow, I'm a total minority here; I'm actually against this because it involves going to the documentation more often because a function is more "magic" than before. When you stare at a piece of code and you can't tell what it's doing because the function name is very short, but the parameters don't tell you what is happening (a good reason for parameter names, but not optional params).
Optional params bloat such functions with leader code that has to deal with the optional elements, when really this "optional" stuff can be handled in alternate functions that do the preparation, and then call the canonical function.
In every language that has optionals that I've coded in, optionals have not made my code easier to maintain, optimise, or even read. In practice it adds bloat to every such function and reduces readability at the call site. It makes API changes harder because now you have many call sites that have different semantics based on arrity, rather than name. This is no good for search and replace let alone refactoring tools.
Nothing good will come with optionals, but param names and varags should not be conflated as the same beast, these do have purposes and it should be seen that it's OK to reject optionals, but implement param names / varargs.

I'm actually against this because it involves going to the documentation more often because a function is more "magic" than before. When you stare at a piece of code and you can't tell what it's doing because the function name is very short, but the parameters don't tell you what is happening (a good reason for parameter names, but not optional params).

Sure, it can be abused, like most language features, but that doesn't mean it doesn't have valid use cases.
Consider the constructor of RenderWindow from SFML. (note that I'm a maintainer of rust-sfml, so I might be biased here)
sf::RenderWindow win(sf::VideoMode(640, 480), "My Application");
It is pretty clear that this creates a RenderWindow with 640x480 resolution and the title "My Application". You don't even need to look at the documentation to figure out what it does. All the defaults for the optional args are very sensible. In fact, they literally call the default constructors of their respective types. The only exception is VideoMode, which provides a very sensible default bit depth of 32.
Now let's consider the alternatives in Rust.
1. Explicit arguments
Do nothing fancy, just require all arguments to be explicit.
let win = RenderWindow::new(VideoMode::new(640, 480, 32), "My Application", Style::default(), ContextSettings::default());
This definitely has the advantage of being explicit about everything, so there can be no misunderstandings. However, the extra explicit arguments given are all defaults. If they weren't explicitly provided, it would be very sensible to assume that the function uses the defaults.
What's the disadvantage? Well, simply put, it's annoying for the user to always have to explicitly provide defaults. If there are a lot of functions like this, it can wear the user down, and make them annoyed with the API. "Why do I always have to say (..., Foo::default(), Bar::default(), ...)? All I want is a window of this size and this title. This is annoying."
Although I do acknowledge, that a "serious" systems programming language might not want to prioritize convenience over explicitness. But Rust can be, and is used for developing applications, games, etc.. If we want to compete with all areas of C++, we might want to consider the convenience features that C++ provides.
2. Different functions for implicit/explicit args
let win = RenderWindow::new(VideoMode::new(640, 480, 32), "My Application");
let win = RenderWindow::new_ext(VideoMode::new(640, 480, 32), "My Application", Style::Fullscreen, ContextSettings::new(...));
Now we have two functions that essentially do the same thing, only one provides sensible defaults, the other is explicit.
This has no advantage over defaulted params. RenderWindow::new does the same "magic" as it would with optional params. Only that there are now 2 function names the user needs to remember. 4 if you wanted to make a different function for all combinations of optional args. Also the required arguments are all repeated in the type signatures for all the different functions. Not a very DRY approach.
3. Builder pattern
This is the most commonly used "substitute" for optional args.
The usage would look like this:
let win = RenderWindowBuilder::new(VideoMode::new(640, 480, 32), "My Application").build();
let win = RenderWindowBuilder::new(VideoMode::new(640, 480, 32), "My Application").style(Style::Fullscreen).context_settings(ContextSettings::new(...)).build();
The advantage here is that it's explicit that this is a "builder", so it has optional args. No "surprise" optional args.
However, it has several disadvantages. The biggest one is API inflation. Now you have to have a RenderWindowBuilder in addition to RenderWindow, and builders everywhere where you wanted to have optional arguments. This leads to a very messy API if you have a lot of optional arguments. Note that I didn't even make a buolder for VideoMode. It would be very ugly to do that just for one optional argument.
It also makes the default case uglier. Now instead of just simply calling a function, the user has to create a builder, and call .build() on it.
This would also look very ugly and unintuitive for functions that aren't constructors. Consider sf::RenderTarget::clear.
The default usage is win.clear(); // clears with black color, sensible default or win.clear(desired_color);.
What would this look like with builders? win.clear_builder().color(desired_color).clear();?
4. Fancy magic with Rust generics
You can do fancy things with Rust traits. For example, you could implement Into<RenderWindow> for different tuples. This is a poor man's version of optional args. It has all the "disadvantages" of optional args, and several more.
The API becomes harder to understand due to all the different generic types involved. The user can't look up the usage in one single place. They have to look for what implements Into<RenderWindow>.
Compile times can potentially suffer, but probably not by much.
And finally, generic methods can't be called on trait objects, so this solution can't work in any context where dynamic dispatch is required.
Addressing the rest of the arguments

Optional params bloat such functions with leader code that has to deal with the optional elements, when really this "optional" stuff can be handled in alternate functions that do the preparation, and then call the canonical function.

This isn't a problem if it's kept simple, like with SFML in the above examples.

In practice it adds bloat to every such function and reduces readability at the call site.

In the above examples, the alternate APIs had more "bloat" than an API with language-level optional args would. Again, readability is not a problem if the provided defaults are sensible.

It makes API changes harder because now you have many call sites that have different semantics based on arrity, rather than name. This is no good for search and replace let alone refactoring tools.

This is also true for many uses of macros and generics, which Rust supports in spite of this.

I would like to see default arguments have the same level of support as default copying of structs.  After all, an argument list is isomorphic to a struct containing all the arguments.  The one wrinkle is that you only want to give a Default trait to part of the list, maybe not all of it. Reasoning this through, it suggests that

Any unspecified default arguments have to be summoned with ..
Arguments can be specified out of order if you specify them by name (but you must specify all of them by name if you do it that way)
The defaultable arguments must always be at the end of the argument list so the division is clear (debatable, but the easiest way to handle "part of the list")
The defaultable arguments look exactly like a struct, i.e. fn example(a: i32, { b: bool, s: String })

This doesn't suggest an obvious way to provide the defaults, however.  The closest analog to an impl section that is specific to a function is its where clause, so perhaps it could go there.  Alternatively, one could use { b: bool = true-type syntax, but then this should be allowed for deriving struct defaults also.
It also doesn't address whether default arguments should be constants or whether they can be computed from the non-default arguments.  Constants are less surprising, but computed defaults can be extremely useful in cases where you would other have to use ad-hoc sentinel values (e.g. if the default should be to match the length of an input).

I've proposed structural records (https://github.com/rust-lang/rfcs/pull/2584), it's not exactly named function arguments, but it overlaps a bit...

Keep in mind that this issue is not just about named args, but also about optional args.

And "variable-arity functions" which probably includes the huge topic of variadic generics. Considering this and the number of comments here, I wonder if this issue should be split into multiple ones.

For those interested in these features, check out https://github.com/rust-lang/rfcs/pull/1806#issuecomment-435680429!
Using #2584 in conjunction with #1806 would provide a solution that I believe would cover most of the use cases for optional/default args.

Not sure if this point has already been raised, but I didn't see it:
For me, rust is all about eradicating classes of issues that plague other languages.
If we have named arguments, we could add a feature to the compiler to eradicate a very common programming bug:
fn doSomething(u8 x, u8 y) {...}

doSomething(y, x); //oh oh, the variables are in the wrong order!

If we have named arguments, we can make the compiler not allow positional arguments in the cases where you have 2 arguments with the same type, i.e the compiler will force you to write:
doSomething(x: x, y: y);

So this bug will never happen again.

@tzachshabtay even better, since in rust struct fields with matching names can and should drop the field: field syntax, this:
doSomething(x: x, y: y);

becaomes this:
doSomething(x, y);

while STILL KEEPING the compile time check that you used the named arguments correctly. WOW.

@berkus However this would mean that changing the name of the fields of a function call would be a breaking change.

Yes, for this the approach taken by Swift lang could be better usable. This thing had been thought out there.

Seems many people agree that macros are appropriate for this task, yet I haven't seen a crate to generate such a macro.
So I wrote a proc_macro duang to implement it.
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);
}

Personally, I would be strongly against optional/named arguments for a language such as Rust. It's a feature that belongs in the space of scripting languages such as python. You can simply use an existing datastructure:
fn optional_arg(optional: Option<i32>) -> i32 {
    match optional {
        Some(a) => a + 1,
        None => 0
    }
}

let arg = Some(1);
let no_arg = None;

let answer = optional_arg(arg);
let answer_default = optional_arg(no_arg);
dbg!(answer, answer_default);

(I should point out in fairness that my example uses dbg! with optional arguments—conceded, that's what macros are for! It's about having a hygienic place for these sorts of language features.)
@crumblingstatue Functions with many unnamed arguments are discouraged, just like how tuples/structs with many unnamed fields are discouraged. Instead, you should use Rust's existing datastructure for named arguments: a struct. Then you could make default constructors that allow users to overwrite fields with Rust's struct update syntax.
struct Window<'a> {
    resolution: VideoMode,
    title: &'a str,
    style: Style,
    settings: ContextSettings,
}
impl<'a> Window<'a> {
    // Non-optional values in method signature
    fn new(vm: VideoMode, s: &'a str) -> Self {
        // optional values in returned struct
        Window {
            resolution: vm,
            title: s,
            style: Style::Fullscreen,         // <-- default!
            settings: ContextSettings::new(), // <-- default also!
        }
    }
}

fn main() {
    let win = Window {
        style: Style::Windowed, // Overwrite default to set an alternative preference
        ..Window::new(VideoMode::new(640, 480, 32), "My Application")
    };
    let win_rendered = RenderWindow::new(win);
}

You can say that this is more verbose, and yeah, the way I've written it is. However—I consider it an advantage that the language lends itself to good API design; Creating the Window struct allows end  users to build a higher-level concept of what RenderWindow::new() is supposed to do. As a bonus, it delineates a potentially helpful refactor; you can replace RenderWindow::new() with the (less Java-y) Window::render(). In essence, the sense that the function signature is too long is an indication that your API needs more building blocks.
I understand how folks coming from python naturally reach for the most 'convenient' syntax to express something, because that's the pythonic style. But there's a difference between syntax that is in line with a language's goals and what is more idiomatic to specifically you. Python is not averse to preferring some really arcane syntax; I feel comfortable writing my_list[:-1][::-1] now that I've been using it for the past few weeks, but when I take breaks from my Python projects and come back to them, I have to look up all the shorthands again. While this suits python's aims of being expressive, it comes at the explicit tradeoff of language consistency. Rust, on the other hand, has very different goals than python. In light of Rust's stated goals of scaleability, maintainability, and building a culture of exceptional documentation, optional/keyword args are not appropriate for the language.

@ThomasKagan You say:

In light of Rust's stated goals of scaleability, maintainability, and building a culture of exceptional documentation, [...].

...but that is precisely the reasons why Rust should have named and default arguments. With only positional arguments, your functions are difficult to maintain, because you will inevitably need to add a new parameter somewhere, which breaks all call sites. The alternative is, of course, to define a new function that also takes the new parameter, but now you have two functions that do almost the same thing. If you then need to add even more optional parameters, there's a combinatorial explosion in the number of functions you need. This does not scale well (in terms of cognitive load) - it dilutes the documentation because you have to explain the difference between all those almost-the-same functions, and anyone reading the calling code needs to spend more effort to keep track of several variations of the same function.
Parameter structs and the builder pattern do solve the same problems, but that is, again, precisely the point - it's a coping mechanism. It definitely has it places, but for many simple cases it's like using dynamite to dig a ditch just because there weren't any shovels around. The fact that we can use design patterns to emulate missing language features is not always a good reason to accept those workarounds as desirable - on the contrary, it's a big reason why Java is so infamously verbose and clunky.
I should note that my main objection to verbose workarounds, like the builder pattern, is not that you have to write a lot of code. It's that you have to read a lot of code. Code that serves only to express implementation details behind the API. Boilerplate is not bad because you have to write it, it's bad because it's irrelevant noise that distracts from the relevant substance of the program.
I agree that functions with too many parameters is usually a code smell, but everything always depends on context, and too many parameters is mainly a problem when the arguments are all mandatory and not named. I think the benefits of named and default arguments far outweigh the risk that it might encourage someone to write functions that do too much.

@ThomasKagan

Then you could make default constructors that allow users to overwrite fields with Rust's struct update syntax.

This doesn't work with structs that have any private fields or which are marked #[non_exhaustive], so it's an incomplete solution at best.

@ThomasKagan

It's a feature that belongs in the space of scripting languages such as python

It works well in Swift and has been in use for ages in Objective-C.

Haha, I figured my view probably wouldn't be the most popular in this crowd. Tbh sometimes it's fun and healthy to be the devil's advocate.
I personally don't consider writing user-facing, public structs bloat if they appropriately model your api's data. I should mention that I would be considerably less averse to, particularly, named default args (because this thread seems to discuss a few different features all as one), for the maintainability reason @emlun mentioned. I see how it could be helpful in Rust's case for accomodating/updating legacy code. In the case of RenderWindow, I would, personally, prefer my Window suggestion, but in theory if you were truly very really sure that a use case needs just a shovel, then fine. But even too many named arguments is a code smell, and for that reason I was hesitant about adding too much nuance to my previous post.
@tikue I don't see how this makes proper data modelling for function args infeasible. But I am open to more comment.
@oblitum Could you delineate how those cases apply to rust as well? Why are those features in Swift and Objective-C, and how do those reasons align with Rust's goals?

@ThomasKagan

@oblitum Could you delineate how those cases apply to rust as well? Why are those features in Swift and Objective-C, and how do those reasons align with Rust's goals?

Read this comment.
It would be great to have this but my actual view is that the boat has sailed for Rust, this should have be in upfront, I think now it's late to have this.
In programming, it's really a sad mistake that the mathematical Euler function notation has become the standard, it's not self documenting, its place is math, not programming. Named parameters and return should have been the norm for programming.

Haha, yeah that's fair. I would like it if all arguments are required to be named, as opposed to the python interpretation where they indicate something is optional and the keyword itself is optional.

I personally see named args and optional args as two different issues. Named args, as previously mentioned, help eliminate entire classes of bugs involving positional args. As soon as you have multiple arguments with the same type, then the type system cannot help you any more.
I agree with the opinion that special Arg structs are one of the current best ways to solve this, but involve both boilerplate and code bloat, so I made a proc macro that generates Arg structs for methods, and then a macro that inserts the strut at the callsite:
#[named_args]
fn foo(a: i32, b: u32, c: String) {
    println!("a: {}", a);
    println!("b: {}", b);
    println!("c: {}", c);
}

pub fn main() {
    named!(foo(a: -4, b: 5, c: "n".to_string()));
}

Since this as implemented is just a syntax switch (what you type inside of the arg brackets gets transplanted into a foo_Args { ... } struct expression) you keep all of the support that struct creation expressions have.
Much like Future vs await, it seems to me that named args are a lot less controversial and can be added first. Furthermore, they IMO add real value just on their own and then we can have a different conversation about optional/variadic arguments.

In terms of syntax for named (not optional) arguments, let's look at the anonymous named struct RFC: https://github.com/Centril/rfcs/blob/rfc/structural-records/text/0000-structural-records.md
| Named version invocation | Positional version invocation
-- | -- | --
Struct | Yes, T { a: A, b: B) | Yes, T(A, B)
Anonymous Type (value) | No, Structural Record RFC (possibly { a: foo, b: bar})  | Yes, (foo, bar)
Function Call | No, this issue | Yes, f(a, b)
Open question 1: should there be a difference between functions with named arguments vs positional arguments? I.E. should named arguments be a semantic difference at the function declaration or just a syntactic one at the call site? Certainly there is a semantic difference between named structs vs positional structs, and probably between named anonymous types and positional anonymous types.
If not, then given that function declarations already have to name their arguments, one possibility is that named argument calls can just reuse those. Then, named arguments would be syntactic sugar over a call to the original function call with the compiler mapping your named arguments into the right position.
Open question 2: how should this interact with arguments named _?
Open question 3: how should this interact with the Fn traits, since the names are lost? Should they just be unable to be called with named arguments?

...given that function declarations already have to name their arguments...

The usual reminder: functions do not have to name there arguments, they provide irrefutable patterns for them. Given a function
fn process_pair( (id, name): (u32, String) ) { unimplemented!() }

that can be written in Rust today, there's no single obvious way named arguments interact with it.

It seems to me then, that for such functions that don't name all their args, there's two possible approaches:

if we allow functions where some arguments are named and some are positional (I personally am against this) then only some of their arguments can be made callable by name, or
Those functions can't be named into name-callable functions and you'd get a compiler error if you tried. Something along the lines of:

The function foo  has non-named arguments and can't be made into a named argument function.
    #[named]
    fn foo(_: i32)
           ^----- this argument does not have a name
    Suggested fix: give the function argument a name like: fn foo(_a: i32)

By the way, this is an interesting question. The leading underscore at the moment is an internal marker for "unused" due to the unused lints skipping variables with leading underscore. However, being able to call the function by argument names freezes them for API stability, which means that adding or removing the leading underscore becomes a breaking change too.
The suggestion above highlights this in an unpleasant way – it suggests that you provide a parameter name explicitly telling the user this parameter is (at least initially) unused. This information is going to be both outdated and provocative ("why do I have to specify this if it's not used anyway?!").
Basically this would deprecate the convention of "underscore-prefix the names of unused parameters" with all the associated churn. The other technically possible way out would be to special-case and ignore leading underscore for the purposes of parameter naming, but that's something I would be strongly against.

...given that function declarations already have to name their arguments...

The usual reminder: functions do not have to name there arguments, they provide irrefutable patterns for them. Given a function
fn process_pair( (id, name): (u32, String) ) { unimplemented!() }

that can be written in Rust today, there's no single obvious way named arguments interact with it.

Something like?:
fn process_pair( pair: (id, name): (u32, String) ) { unimplemented!() }
process_pair(pair: (42, "something"));

I don't see a problem except for the overloaded use of the colon, maybe some alternative syntax is worth considering.

Swift use space for argument labels:

https://docs.swift.org/swift-book/LanguageGuide/Functions.html#ID167

I'll go into detail on each one:

keyword-based parameters (as opposed to position-based parameters)
It'd be a nice addition but it doesn't interact well with tuple destructuring. Actually, I think functions with many parameters is in most cases an anti-pattern and I favor creating types liberally to give your code context through the type system and write "small" functions (self-documenting code FTW).

optional parameters (where one provides a default value for them, usually in the parameter list)
Pass an Option<T>, call .unwrap_or() at the start of the function's body and shadow that variable. Not as sugary but a lot more explicit, which is something that Rust really focuses on. I'm against implementing any kind of syntatic sugar for this, it isn't worth it.

variable-arity functions (which can be seen as a generalization or variation on optional parameters, depending on how you look at it)
I hate this in C and how it's just a language-specific way to walk over a variable-sized list with no names. I very much prefer the Python way, and we can do that by passing any collection type with the requirements we need and knowing all the performance and feature trade-offs. Again, a lot more explicit and I'm specially against this one.

What I'd really like to see is functions with multiple signatures that have the same types but different destructuring patterns, so for those cases where you currently write a match block that covers the whole function depending on some arguments, you don't use that block.

It'd be a nice addition but it doesn't interact well with tuple destructuring.

🤔, the previous comments were just asking why is that and provided examples demonstrating that doesn't look like a real issue.

Actually, I think functions with many parameters is in most cases an anti-pattern and I favor creating types liberally to give your code context through the type system and write "small" functions (self-documenting code FTW).

https://github.com/rust-lang/rfcs/issues/323#issuecomment-508955630

FWIW, Gleam, a statically typed language for the Erlang VM, written in Rust, follows Swift at providing labeled arguments:
https://gleam.run/tour/function.html#labelled-arguments

🤔, the previous comments were just asking why is that and provided examples demonstrating that doesn't look like a real issue.

I read that. For consistency that would require to bring the whole argument label/parameter name thing, which I'm not sure I like. But if it's backwards-compatible and optional, I'm not totally opposed to it, just not interested enough.

FWIW, Gleam, a statically typed language for the Erlang VM, written in Rust, follows Swift at providing labeled arguments

So...?

So...?

No argument, hence FWIW.

@ThomasKagan

Personally, I would be strongly against optional/named arguments for a language such as Rust. It's a feature that belongs in the space of scripting languages such as python

There are default arguments in C++, Kotlin has named arguments and default arguments. I don't see anything wrong with this, that some useful things come to us from scripting languages. Probably, the default arguments aren't so necessary, but having named arguments would be a great feature of Rust. Position-independent arguments improve the durability of the public and internal API and will not allow it to be broken due to ridiculous arguments movements or adding arguments.

Probably, the default arguments aren't so necessary, but having named arguments would be a great feature of Rust. Position-independent arguments improve the durability of the public and internal API and will not allow it to be broken due to ridiculous arguments movements or adding arguments.

The beautiful thing about the combination of named and optional arguments is that designing API's with a lot of parameters gets much easier.
Especially for cases where you have 3+ boolean parameters that are all optional it's just very neat to work with positional parameters.
fn search(query: String, include_images: bool, include_news: bool, use_cache: bool)

search("foo", false, true, false)
search("foo", false, false, true)

// vs.

// Using the star to signal that the rest are keyword arguments
fn search(query: String, *, include_images: bool=false, include_news: bool=false, use_cache: bool=false)

search("foo", include_news=true)
search("foo", use_cache=true)

Not only is the latter more readable, it's way less error prone. This is a very simple proposal and resembles Python, but the * could obviously be replaced by pretty much anything (e.g. a pub or a ~, ^, @ or whatever).
I would probably advice against mixing positional/keyword arguments (unlike Python, where almost all arguments are potential keyword arguments). This makes it so parameter names in older Rust code won't suddenly become part of the API.

So basically we're just talking about some potential special sugar for
do_thing(ThingArgs{ use_cache: true, include_news: false});

Right?

Yes, I guess pretty much everything is possible already if you combine functions and structs.
Another way to write my proposal would be to work with something like "anonymous" structs, which could look like this: search("foo", {use_cache: true, include_news: false});. IMO this is just uglier, but it would probably serve the same purpose.
For the compiler the easiest way to understand this would probably to create "anonymous" structs.

@Lokathor
The difference is that we don’t need to pack arguments into a structure.

For each function you have to create own super-duper structure. It's verbose.
It requires "come up" with this in advance. But what if before that we used positional arguments? Next, we decided to simulate "named arguments" and introduced a structure. This breaks API, because now the calling code must pass the structure instead of separate arguments.
This increases the cognitive load. Using the structure as an argument, I need to look at the structure fields before calling the function; it’s not enough for me to look at the function signature.

Yes, I guess pretty much everything is possible already if you combine functions and structs.

I tried wrapping my head around the idea of naming things that implement a trait without success:
fn foo(i: i32, get_int: impl Fn() -> i32, consume_int: impl Fn(i32));
foo(3, || 45, |x| println!("{:?}", x));

Versus something like:
fn foo(i: i32, pub get_int: impl Fn() -> i32, pub consume_int: impl Fn(i32));
foo(3, get_int: || 45, consume_int: |x| println!("{:?}", x));

Since struct can't have field that impl trait, it's not possible to use the builder pattern with no overhead (you will have dynamic dispatch with dyn instead of static dispatch).
Trait aliases aren't a solution either.

@Lokathor - Function arguments' lifetimes aren't treated the same way as struct field lifetimes, which is another way that it's not just sugar.  (Or it's a lot more sugar than what you showed, including struct creation, automatic handling of lifetimes, and destructuring to get at all the pieces.)

@robinmoussu It is possible using a struct, it simply needs to be a generic struct (Playground):
struct FooOptions<F, G> {
    get_int: F,
    consume_int: G,
}

fn foo<F, G>(
    i: i32,
    FooOptions {
        get_int,
        consume_int,
    }: FooOptions<F, G>,
) where
    F: Fn() -> i32,
    G: Fn(i32),
{
    // ...
}

For information, I finally managed to have named argument with default value for impl trait in rust stable.

Wall of code (click to expand)

This works in stable
// It's needed to create a separate type with a similar name than get_int
pub struct GetInt<F>(F)
where F: Fn() -> i32;

// Cannot use Fn() -> i32, must be a concrete type
impl Default for GetInt<fn() -> i32>
{
    fn default() -> Self{ GetInt(|| 1) }
}

pub struct ConsumeInt<F>(F)
where F: Fn(i32);

// Cannot use Fn() -> i32, must be a concrete type
impl Default for ConsumeInt<fn(i32)>
{
    fn default() -> Self{ ConsumeInt(|x| println!("{:?}", x)) }
}

pub fn foo
(
    i: i32,
    get_int: GetInt<impl Fn() -> i32>, // Repetition of the impl trait
    consume_int: ConsumeInt<impl Fn(i32)>
)
{
    for _ in 0..i {
        consume_int.0(get_int.0()); // Horrible .0 syntax
    }
}

fn main() {
    foo(
        3,
        GetInt(|| 45),
        ConsumeInt(|x| println!("{:#}", x)),
    );
    foo(
        2,
        GetInt::default(),
        ConsumeInt::default(),
    );
}

This would be much better
pub fn foo
(
    i: i32,
    pub get_int: impl Fn() -> i32 = || 1,
    pub consume_int: impl Fn(i32) = |x| println!("{:?}", x)
)
{
    for _ in 0..i {
        consume_int(get_int()); // note: no .0
    }
}

fn main() {
    foo(
        3,
        get_int: || 45,
        consume_int: |x| println!("{:#}", x),
    );
    foo(
        2,
        get_int::default(),     // note: placeholder syntax
        consume_int::default(),
    );
}

For each named, defaultable argument, this adds:

6 lines of boilerplate
an additional public type.

It works, but to be honest it really reminds me the bad part of C++ template meta-programming.

For each named, defaultable argument, this adds:>

6 lines of boilerplate
an additional public type.

Hmmm, perhaps this can be turned into something practical by some sort of magic macro?

@norru This is not a realistic option, and has been discussed over and oven in this thread. @david-mcgillicuddy-moixa did a POC as well as @xiaoniu-578fa6bff964d005 here. Proc macro are bandage that are ok-ish for your own application crate. But as soon as you want to import them from an external crate, it's not really convenient to use. I don't expect either that I could use those macro if I try to contribute to a crate I'm not the maintainer.

This discussion starts to be long, I will try to sum-up what I found the most interesting:
It's worth noting that named arguments, default arguments and variable-arity functions can all be implemented separately. I will only talk about named arguments and default arguments since I think they are the most important feature to have.

I think that @crumblingstatue's comment explains the best why having default argument is important.
Probably what was the best proposition so far for named argument was done by @azerupi and has done a pre-RFC. It has a parsing ambiguity (as explain in the summary with type ascription, but it's not too late since type ascription is not in stable yet (it's only feature gated), and not widely used. I personally think it would be better to change the syntax of type ascription to allow named arguments.
To give you the gist:
// Normal function with positional arguments
pub fn positional(a: i32, b: i32) -> i32;

// Function with named arguments
pub fn named(pub a: i32, pub b: i32) -> i32;

This can be easily extended to support default value for arguments:
// Function with default named arguments
pub fn default_arguments(pub a: i32 = 0, pub b: i32 = 2) -> i32;

More details could be found in the mentioned pre-RFC.

And to add my own opinion, I think that (like in C++), default argument should just be syntactic sugar at the call site (ie. copying the expression of the "initializer" at the call site) witch simplify a lot of the issue.
pub fn(a: int, foo: SomeType = bar("baz")) { ... }

// when writting
foo(42);
// it will be expanded at compile time to
foo(42, bar("baz"));

At call site, to allow future addition of new arguments with default value, no additional syntax should be needed, but a trailing , .. could be added to be explicit and warned by default with clippy:
// this compiles but clippy warns about it
foo(42);
// this compiles and emit no warning
foo(42, ..);

Note: .. is syntax for a full range

Probably what was the best proposition so far for named argument was done by @azerupi and has done a pre-RFC. It has a parsing ambiguity (as explain in the summary with type ascription, but it's not too late since type ascription is not in stable yet (it's only feature gated), and not widely used. I personally think it would be better to change the syntax of type ascription to allow named arguments.

I think type ascription (which we already have, e.g. in let pat : type = expr; or fn foo(pat : type)) is substantially more important than named arguments and I think that type ascription should uniformly use the obvious term : type syntax as in most similar languages. So I do not agree with having type ascription on expressions change its syntax, especially when https://github.com/rust-lang/rfcs/pull/2584 (structural records) give you named arguments in a principled way, which works well with the type system overall, and is generally more useful. So all in all, I think I'm not in favor of named arguments as something other than structural records (or possibly anonymous types, but they are less useful).

For those who wants the context but don't want to open external links, #2584:

Introduce structural records of the form { foo: 1u8, bar: true } of type { foo: u8, bar: bool } into the language. Another way to understand these sorts of objects is to think of them as "tuples with named fields", "unnamed structs", or "anonymous structs".

Specifically about named arguments:

Structural records could be considered to lessen the need for named function arguments by writing in the following style:
fn foo(bar: { baz: u8, quux: u8 }) -> u8 {
   bar.baz + bar.quux
}

fn main() {
    assert_eq!(3, foo({ baz: 1, quux: 2 }));
}

With those limitations:

With structural records, you cannot construct them positionally.
You cannot adapt existing standard library functions to use named arguments.

@Centril First of all, I would like to apologize, for some reason when I read this whole thread I didn't really like (initially) your proposition, and kind of missed it.
I think it solve the issue of named argument, and is totally compatible with a (possible) future addition of default value (like your proposition on default value for struct).
I also don't think that the limitations you mentioned are an issue, because it can be easily solve in the future. If rust ever get something like star pattern in python (ie. expanding the content of a struct in-place) both of those issue disappears. If expanding was done through a magic macro expand! witch sort the arguments as the enclosing context expect, we could write:
fn foo(bar: u8, baz: u8);

let foo_args = {baz: 2, bar: 1}; // Same name, but not the same order the the args of foo()
foo(expand!(foo_args)); // expanded as foo(1, 2)

All in all, is there somewhere an overview/ETA of the global progress of all those features? I tried to do it in this thread, but it was quite hard to find the current state of all discussions and implementation.

All in all, is there somewhere an overview/ETA of the global progress of all those features? I tried to do it in this thread, but it was quite hard to find the current state of all discussions and implementation.

Not beyond what is noted in my draft (which I will get to finishing eventually) and the structural records RFC. As you may imagine, the language and compiler teams are swamped with work already, cleaning up years of technical debt.

Structural records might be useful in some cases, but I think REAL keyword args (together with REAL optional / default args and variadic args) looks more clear and is easier to use.

I don't particularly agree with this perspective. I find foo(param: expr) to be confusing and to fit poorly with the general design of the language due to the trait system, patterns, etc. Moreover, "keyword args" as construed here is also limiting and far less powerful than structural records. In particular, while structural records permit building up arguments first into a let binding, grouping things into different records, more generally treating the inputs as a data type, keyword args do not.

On the other hand, using structural records to emulate keyword arguments makes it a tradeoff.  You have to decide, "this function has a ton of arguments, so it's worth making it accept a record instead, even at the cost of making all callers use the ugly func({args}) syntax and requiring all arguments to be labeled."  You can't just say, "hey, this bool argument is a little unclear, so I'll just tack on a label".  (Or if you don't like the bool example because you can use enums instead, how about: "hey, in this copy-file function the user might forget which argument is source and which is destination, so I'll label them".)  That's why I dislike the structural-record approach.  If you're prepared to require extra ceremony, named records work pretty well as it is.

I think that's probably a good thing though. You should really be avoiding designing function calls so complex that you'd set up a full struct as the args in the first place. Not that there's never a place for such a thing, but that it's something to really really avoid.

That's some to really really avoid, but I like to point out that this line of reasoning should simply be put fully aside when actually designing the feature or a language, the design shouldn't be driven from worst cases, otherwise the language would simple provide no feature at all, as any feature characteristics can be abused. That's the job for language best practices.

I do not agree whatsoever that my_fun({ foo, bar, baz }); is ugly. The syntactic overhead is minimal. Moreover, and unlike named arguments, you can build up each bit in let statements and then use the field shorthand mechanism to avoid having to write foo(name: value, ...).
I tend to agree with @Lokathor that a function with so many parameters is a poorly designed one, and there are limits to how convenient we should make such poor designs. I do also think that requiring names at the definition site is fine. When there is such a bool parameter (not that I would find that good design), past experience tells me that labeling just one call site would be insufficient.

I tend to agree with @Lokathor that a function with so many parameters is a poorly designed one, and there are limits to how convenient we should make such poor designs.

True keyword arguments can help functions with any number of parameters, such as foo(true) or copy(source, dest).  You could put them on all your functions if you wanted.  It's only the structural-record workaround that, by giving "keyword arguments" syntactic overhead, assumes they should be reserved for extraordinary cases, like functions with large numbers of parameters.

When there is such a bool parameter (not that I would find that good design), past experience tells me that labeling just one call site would be insufficient.

Well, the space of possible designs includes ones where the keyword would be mandatory at call sites, similar to Swift (and to Rust structs).  That admittedly would have the significant downside that keywords couldn't be retrofitted onto existing functions, but it avoids the "just one call site" issue.  (Note that requiring keywords doesn't contradict my earlier objection to "requiring all arguments to be labeled", since you could mark only a subset of a function's arguments as keyword arguments.)
Even if keywords are optional at call sites, my past experience (from Python) tells me that labeling bool arguments can work just fine.  It's not hard to remember to label them at all call sites.

It's only the structural-record workaround that, by giving "keyword arguments" syntactic overhead, effectively assumes they should be reserved for extraordinary cases, like functions with large numbers of parameters.

On the other hand, field shorthands combined with structural records reduce overall syntactic overhead as compared to keyword arguments at call sites. I also don't agree that the minor overhead involved would result in usage restricted to functions with large number of parameters. I would personally use structural records for semantic grouping, whether there be a small or large number of parameters (but preferably the former).

Even if keywords are optional at call sites, my past experience (from Python) tells me that labeling bool arguments can work just fine. It's not hard to remember to label them at all call sites.

Labeling bool arguments only tells you what the meaning of that argument is within the function (the "what"). It does not however tell you the "why" of passing true in this specific location, which is, in my experience, far more important.

I would love this as someone who likes to write libraries something like
fn color_print(text: &str, bold: bool=false, underline: bool = false) {
    // ASCII STUFF
}

Could be called as
color_print("Hello", underline=true);

instead of having to define bold as well

Is there a time table on this?

This issue is all about discussing this topic, and there's no timeline to it ever becoming supported syntax at the moment.