# Pre-RFC: Named arguments

**URL:** <https://internals.rust-lang.org/t/pre-rfc-named-arguments/16413>\
**Category:** language design\
**Created:** [April 2, 2022, 9:27pm UTC](https://internals.rust-lang.org/t/pre-rfc-named-arguments/16413 "2022-04-02T21:27:07Z")\
**Posts on this page:** 20\
**Page:** 4

<div class="post-metadata">

**Author:** ![gbutler](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/gbutler/32/3670_2.png) [@gbutler](https://internals.rust-lang.org/u/gbutler)\
**Post date:** [April 10, 2022, 12:04am UTC](https://internals.rust-lang.org/t/pre-rfc-named-arguments/16413/61 "2022-04-10T00:04:03Z")

</div>

> [@anon32976453](#):
>
> Adding a whole new alternative design is the heavy handed situation.

Adding this to the language only needs done once. Having to write a new-type for every parameter where the new-type does nothing but wrap the actual type (that has no additional constraints) for purposes of naming it is definitely "Heavy Handed" IMHO.

> [@anon32976453](#):
>
> I must question though, if you think using types is so heavy handed, why are you using Rust in the first place? Surely, a dynamic language like Python would fit better with your particular set of design considerations.

Types and Names are orthogonal. You want me to use a type where a name is what I need and what is useful.

> [@anon32976453](#):
>
> Put another way, asking a pizza restaurant to serve dumplings is a bit more tenuous than having said place serve more kinds of pizza toppings. Of course, there's nothing wrong with wanting dumplings, you can always just go to a different restaurant that specialises in that kind of cuisine.

I find these kinds of analogies to be highly unhelpful and add nothing to the debate.

---

<div class="post-metadata">

**Author:** ![anon32976453](https://avatars.discourse-cdn.com/v4/letter/a/49beb7/32.png) [@anon32976453](https://internals.rust-lang.org/u/anon32976453)\
**Post date:** [April 10, 2022, 12:25am UTC](https://internals.rust-lang.org/t/pre-rfc-named-arguments/16413/62 "2022-04-10T00:25:11Z")

</div>

> [@gbutler](#):
>
> Types and Names are orthogonal. You want me to use a type where a name is what I need and what is useful.

That is incredibly false. Types provide semantic meaning which is what we use names for in natural languages. They are not orthogonal by any stretch of the imagination.

Here's a classic example of this:

both metres and feet are numbers. They are the same "type" by your definition, just with different names. If we go by that than it is easy to get to those famous examples such as the failed NASA rover mission to Mars.

Rust is supposed to be a safe language. Having a function:

```
fn do_something(timeout_secs: i32);

```

Is therefore unsafe and bad design compared to something like:

```
fn do_something(timeout: Seconds); 

```

Or even better:

```
fn do_something(timeout: Timespan);

```

Adding named parameters to Rust goes against one of its core principles imo.

---

<div class="post-metadata">

**Author:** ![gbutler](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/gbutler/32/3670_2.png) [@gbutler](https://internals.rust-lang.org/u/gbutler)\
**Post date:** [April 10, 2022, 12:29am UTC](https://internals.rust-lang.org/t/pre-rfc-named-arguments/16413/63 "2022-04-10T00:29:12Z")

</div>

```rust
fn do_something( timeout_secs: u32, repeat_secs: u32 );
fn do_something( timeout: Seconds, repeat: Seconds);

```

Would you have me do?

```rust
fn do_something( timeout: TimeoutSeconds, repeat: RepeatSeconds )

```

Just so I can have things read clearly at the call site?

---

<div class="post-metadata">

**Author:** ![anon32976453](https://avatars.discourse-cdn.com/v4/letter/a/49beb7/32.png) [@anon32976453](https://internals.rust-lang.org/u/anon32976453)\
**Post date:** [April 10, 2022, 12:38am UTC](https://internals.rust-lang.org/t/pre-rfc-named-arguments/16413/64 "2022-04-10T00:38:47Z")

</div>

> [@gbutler](#):
>
> Adding this to the language only needs done once. Having to write a new-type for every parameter where the new-type does nothing but wrap the actual type (that has no additional constraints) for purposes of naming it is definitely "Heavy Handed" IMHO.

If you want a language that allows for quick prototyping and saves you time on typing than a dynamic language like Python is your best bet!

Again, I'm not arguing against such a use case. Just that it is inappropriate to try to shoehorn this into a language designed for a different set of trade-offs.

Rust requires more typing, yes. It is the price we must pay for the added benefit of safety. This includes the ownership system as well as adding semantic types so that the compiler will verify statically the correctness of the code.

In my previous example, that I cannot pass a number of "metres" to a function that semantically expects to operate on a number of "feet".

Simply naming the parameters differently does not work with such static verification.

---

<div class="post-metadata">

**Author:** ![anon32976453](https://avatars.discourse-cdn.com/v4/letter/a/49beb7/32.png) [@anon32976453](https://internals.rust-lang.org/u/anon32976453)\
**Post date:** [April 10, 2022, 12:47am UTC](https://internals.rust-lang.org/t/pre-rfc-named-arguments/16413/65 "2022-04-10T00:47:26Z")

</div>

> [@gbutler](#):
>
> ```rust
> fn do_something( timeout_secs: u32, repeat_secs: u32 );
> fn do_something( timeout: Seconds, repeat: Seconds);
> 
> ```
> 
> Would you have me do?
> 
> ```rust
> fn do_something( timeout: TimeoutSeconds, repeat: RepeatSeconds )
> 
> ```
> 
> Just so I can have things read clearly at the call site?

I reckon that's a contrived example and a straw man argument which I've already addressed in my previous posts. You can still define a single struct with two fields. No one's forcing your hands to have a unique type per each parameter.

---

<div class="post-metadata">

**Author:** ![gbutler](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/gbutler/32/3670_2.png) [@gbutler](https://internals.rust-lang.org/u/gbutler)\
**Post date:** [April 10, 2022, 12:51am UTC](https://internals.rust-lang.org/t/pre-rfc-named-arguments/16413/66 "2022-04-10T00:51:29Z")

</div>

> [@anon32976453](#):
>
> I reckon that's a contrived example and a straw man argument

> [@anon32976453](#):
>
> No one's forcing your hands

I think we're arguing past one another and so I'm just going to leave it there.

---

<div class="post-metadata">

**Author:** ![zackw](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/zackw/32/2071_2.png) [@zackw](https://internals.rust-lang.org/u/zackw)\
**Post date:** [April 10, 2022, 1:52am UTC](https://internals.rust-lang.org/t/pre-rfc-named-arguments/16413/67 "2022-04-10T01:52:50Z")

</div>

> [@gbutler](#):
>
> I can't think of any _mainstream_ programming language that overloads simply on arity.

Overloading based on arity alone is isomorphic with optional/default arguments, and there are lots of languages with optional/default arguments.

The same logic allows me to observe that Rust _already has_ overloading based on the type of one (specially marked in the syntax) argument; we just call it dispatching to a trait implementation. Which means that it is _already_ necessary to know the type of that argument in order to read code. Yes, a trait comes with an interface contract — every implementation of `std::ops::Add` is _supposed to_ perform addition, for some definition of "addition" — but that is only enforced by convention, and there's tons of room for "creative" interpretation of the documentation.

---

<div class="post-metadata">

**Author:** ![RalfJung](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/ralfjung/32/2415_2.png) [@RalfJung](https://internals.rust-lang.org/u/RalfJung)\
**Post date:** [April 10, 2022, 1:55am UTC](https://internals.rust-lang.org/t/pre-rfc-named-arguments/16413/68 "2022-04-10T01:55:54Z")

</div>

> [@gbutler](#):
>
> But, I'm going to try to argue a few nits with you because I do feel pretty strongly about them.

Please absolutely do! I can't emphasize enough how much I don't want to "win arguments by authority".

Also, I am sorry if my wording was too confrontational or otherwise inappropriate. Thanks for your respectful reply!

> [@gbutler](#):
>
> I really consider that "overloading" based on arity vs. types vs. parameter names are entirely different animals and objections that apply to one don't necessarily apply to the others.

I can see how overloading based on types can feel quite different since (in particular in Rust) it is a lot more implicit.

I would still argue that any kind of overloading has other issues besides implicitness, like making things harder to parse for humans (the `fun` part of `fun(args)` is no longer all the information you need) and making it more awkward to _refer to_ a function rather than calling one. The RFC has to go through some hoops like `foo(a:b:)` to resolve this, and that feels somewhat clunky.

Of course, these are all very subjective value judgments. What I would really like to understand is why y'all think that named arguments _need_ this name-based overloading to be a good/useful addition to Rust. Some concrete examples would help.

The example in the RFC is about replacing `r.ok_or(foo)` with `r.ok(or: foo)`. I don't think that is a very compelling usecase for named arguments (`ok_or` seems perfectly adequate), so I am not convinced by this example that we need name-based overloading. To me, the best motivations for named arguments are functions like the `CompileFilter::new` that the RFC mentions, and almost every case of a function taking a `bool` as an argument. We have such functions now and it's not great (e.g., using comments to explain what the arguments are), but having to give a unique name to each function does not seem to be a problem. Why does it become a problem when arguments can be named at the call site? Some more examples would be really good, seeing as overloading and the complexities it creates take up a large fraction of the RFC.

FWIW, I could totally see a good case for _default values_ for arguments, once there are named arguments. However, that can be done without name-based overloading, so it is a lot simpler. So to me, a convincing example for name-based overloading should be one that cannot be handled well with argument default values. The RFC states that default values are orthogonal to named arguments and hence out of scope. I don't disagree, but I consider overloading equally orthogonal. I think default values have nice synergies with named arguments -- you can give any subset of the arguments in any order you like, and things just work as they should (kind of like a built-in builder pattern). I understand you feel the same about synergies between named arguments and name-based overloading. 🙂

---

<div class="post-metadata">

**Author:** ![CAD97](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/cad97/32/3460_2.png) [@CAD97](https://internals.rust-lang.org/u/CAD97)\
**Post date:** [April 10, 2022, 5:08am UTC](https://internals.rust-lang.org/t/pre-rfc-named-arguments/16413/69 "2022-04-10T05:08:54Z")

</div>

The _biggest_ argument for label-based overloading is being able to upgrade std APIs in place to use labels. Perhaps that can be addressed via (edition) optional labels? This would sidestep the need to name multiple functions from the same base path, because they'd be the same item. Some migration path to allow us to add labels to std functions I think is undebatably required in an argument labels proposal, to avoid old std functions feeling second class.

The second argument is more debatable: supporting the same verb (function path) but with different labeling. And I actually can't come up with a good example that isn't a form of optional/default arguments other than `new(in:)` versus `new_in(_:)` (though it's been a long time since I've used Swift, and didn't go digging through the docs).

I have a theory: given any existing argument label overload set that logically fits under a single verb, they can be partitioned into a set of optional arguments sets based solely on the label of the first argument. (Thus, if you put the first argument's label as part of the function name, you no longer need label based overloading.) I would _honestly_ like to see a Swift counterexample to this theory in a library's public API.

(It's maybe worth noting that this has literally just changed the design for my in-my-head "perfect for me" language. I like argument labels, but avoiding needing `f(arg:)` overload set resolution is enticingly beneficial, as you no longer have the nullary function special case.)

---

<div class="post-metadata">

**Author:** ![josh](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/josh/32/5934_2.png) [@josh](https://internals.rust-lang.org/u/josh)\
**Post date:** [April 10, 2022, 6:38am UTC](https://internals.rust-lang.org/t/pre-rfc-named-arguments/16413/70 "2022-04-10T06:38:24Z")

</div>

> [@CAD97](#):
>
> The _biggest_ argument for label-based overloading is being able to upgrade std APIs in place to use labels. Perhaps that can be addressed via (edition) optional labels?

Agreed; we could have a `#[optional_name]` mechanism if need be.

---

<div class="post-metadata">

**Author:** ![gbutler](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/gbutler/32/3670_2.png) [@gbutler](https://internals.rust-lang.org/u/gbutler)\
**Post date:** [April 10, 2022, 12:59pm UTC](https://internals.rust-lang.org/t/pre-rfc-named-arguments/16413/71 "2022-04-10T12:59:05Z")

</div>

> [@CAD97](#):
>
> The second argument is more debatable: supporting the same verb (function path) but with different labeling. And I actually can't come up with a good example that isn't a form of optional/default arguments other than `new(in:)` versus `new_in(_:)` (though it's been a long time since I've used Swift, and didn't go digging through the docs).

Yes, the more I think about it, the more I tend to agree with you here. Every example I can think of where I would want the same verb but differing arguments is best supported by either Optional/Default arguments or Generics.

---

<div class="post-metadata">

**Author:** ![gbutler](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/gbutler/32/3670_2.png) [@gbutler](https://internals.rust-lang.org/u/gbutler)\
**Post date:** [April 10, 2022, 1:04pm UTC](https://internals.rust-lang.org/t/pre-rfc-named-arguments/16413/72 "2022-04-10T13:04:03Z")

</div>

> [@zackw](#):
>
> Overloading based on arity alone is isomorphic with optional/default arguments,

I don't think this changes your overall point that much but I disagree with this. I could have a function with 2 arguments and 1 with 3. The 2 argument function could have entirely different types than the one with 3. ~~There is no way to model that with overloading by arity alone.~~ What I meant to say is, there is no way to model that by just default/optional arguments.

---

<div class="post-metadata">

**Author:** ![zackw](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/zackw/32/2071_2.png) [@zackw](https://internals.rust-lang.org/u/zackw)\
**Post date:** [April 10, 2022, 1:17pm UTC](https://internals.rust-lang.org/t/pre-rfc-named-arguments/16413/73 "2022-04-10T13:17:53Z")

</div>

> [@gbutler](#):
>
> I could have a function with 2 arguments and 1 with 3. **The 2 argument function could have entirely different types than the one with 3.**

(boldface - my emphasis) I don't think it counts as "overloading by arity _alone_" anymore if you allow the types of the arguments to change at the same time as the arity changes.

---

<div class="post-metadata">

**Author:** ![gbutler](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/gbutler/32/3670_2.png) [@gbutler](https://internals.rust-lang.org/u/gbutler)\
**Post date:** [April 10, 2022, 1:21pm UTC](https://internals.rust-lang.org/t/pre-rfc-named-arguments/16413/74 "2022-04-10T13:21:12Z")

</div>

> [@zackw](#):
>
> I don't think it counts as "overloading by arity _alone_

The function with 2 arguments is one version, the one with 3 is another version of the same root function name. I've only considered arity. However, if I want to model the same situation with default/optional arguments, I can't; therefore, Default/Optional arguments are not isomorphic with arity-based overloading (to my mind).

---

<div class="post-metadata">

**Author:** ![zackw](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/zackw/32/2071_2.png) [@zackw](https://internals.rust-lang.org/u/zackw)\
**Post date:** [April 10, 2022, 1:25pm UTC](https://internals.rust-lang.org/t/pre-rfc-named-arguments/16413/75 "2022-04-10T13:25:25Z")

</div>

I agree that default/optional arguments cannot model the situation where the first two arguments of the 3-argument function aren't required to have the same types as the arguments to the 2-argument function with the same name. However, to my mind, not imposing that requirement means that the language feature we're talking about is now _type-based_ overloading with the additional restriction that the arity has to change at the same time.

---

<div class="post-metadata">

**Author:** ![RalfJung](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/ralfjung/32/2415_2.png) [@RalfJung](https://internals.rust-lang.org/u/RalfJung)\
**Post date:** [April 10, 2022, 2:42pm UTC](https://internals.rust-lang.org/t/pre-rfc-named-arguments/16413/76 "2022-04-10T14:42:39Z")

</div>

> [@CAD97](#):
>
> The _biggest_ argument for label-based overloading is being able to upgrade std APIs in place to use labels.

I feel like I must be missing something, why does that need overloading? Why can't we just add labels to the existing functions, keeping their name and not do overloading? That would mean support for calling named argument functions with non-named call sites. Is that so bad? Sure, we might forget to port our code (or forget the names in new code), but if we add named and non-named overloads, the same is true.

(If the RFC discusses this then I didn't find it. The "alternatives" section doesn't even mention not doing overloading, so all the arguments for overloading are spread across the entire document, making them hard to find. That said, I know RFCs are tricky to write, and this one is written very well! I just wish it would take not doing overloading a bit more serious, and have a coherent argument in a single location for why it considers that not an option.)

> [@josh](#):
>
> Agreed; we could have a `#[optional_name]` mechanism if need be.

Oh I see, the concern is that for _new_ APIs with names, we don't want people calling them without names.

Yeah that should be covered by letting the function author decide whether names are mandatory or not. I agree an attribute works here, but this is yet another mechanism, so the complexity budget on the "no overloading" side is slowly stacking up (we got default values and optionally-optional-names there now)...

---

<div class="post-metadata">

**Author:** ![gbutler](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/gbutler/32/3670_2.png) [@gbutler](https://internals.rust-lang.org/u/gbutler)\
**Post date:** [April 10, 2022, 3:30pm UTC](https://internals.rust-lang.org/t/pre-rfc-named-arguments/16413/77 "2022-04-10T15:30:04Z")

</div>

> [@RalfJung](#):
>
> I feel like I must be missing something, why does that need overloading? Why can't we just add labels to the existing functions, keeping their name and not do overloading?

I think something like this is the motivating example:

Let's say you have:

```rust
fn new( ... ) -> ...;
fn new_with( ... ) -> ...;

```

and you'd like to have:

```rust
fn new( from ... );
fn new( from... , with ... );

```

You don't want to have to have instead:

```rust
fn new(from ... );
fn new_with( from ..., with ... );

```

Now the above is a totally bad example but I think it illustrates how you'd like to not have to repeat the parameter "name" in the name of the function.

---

<div class="post-metadata">

**Author:** ![RalfJung](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/ralfjung/32/2415_2.png) [@RalfJung](https://internals.rust-lang.org/u/RalfJung)\
**Post date:** [April 10, 2022, 3:53pm UTC](https://internals.rust-lang.org/t/pre-rfc-named-arguments/16413/78 "2022-04-10T15:53:13Z")

</div>

Wouldn't that be handled by having a default for `with`?

---

<div class="post-metadata">

**Author:** ![gbutler](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/gbutler/32/3670_2.png) [@gbutler](https://internals.rust-lang.org/u/gbutler)\
**Post date:** [April 10, 2022, 3:58pm UTC](https://internals.rust-lang.org/t/pre-rfc-named-arguments/16413/79 "2022-04-10T15:58:08Z")

</div>

Yes it would. That's why I agreed above that any need for overloading by parameter names is probably better served by an Optional/Default arguments feature and/or by the use of generics.

I was just trying to explain what my understanding of the motivation for saying overloading by parameter name was needed in order to allow for smooth porting of existing libraries.

The more I think about it though and try to come up with examples, the more I realize that all the examples I can think of can either be served by Optional/Default arguments or by the use of Generic code (or a combination of the 2).

TL;DR: I've been convinced that overloading isn't needed to make this viable/useful. However, it seems like Default/Optional arguments should be a separate RFC but should strongly be considered as being implemented along with this or shortly thereafter.

---

<div class="post-metadata">

**Author:** ![jrose](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/jrose/32/9591_2.png) [@jrose](https://internals.rust-lang.org/u/jrose)\
**Post date:** [April 11, 2022, 5:20am UTC](https://internals.rust-lang.org/t/pre-rfc-named-arguments/16413/80 "2022-04-11T05:20:29Z")

</div>

Sure. Option A, the one I recommend, is that function types do not have labels. The type of `fn removeAll(&mut self, equalTo value: T)` and the type of `fn append(&mut self, _ value: T)` are both `fn(&mut Self, T)`.

Option B is to keep the labels in the type, giving you `fn(&mut Self, equalTo: T)` and `fn(&mut Self, T)`. From my perspective, this has a few problems:

- The labels don’t necessarily make sense without the base name, and when you’re using function pointers (or function traits), you’ve got more than one possible value, with no guarantee they have the same labels. Heck, while I’ve never seen functions actually named `add(to: Set<T>)` and `remove(from: Set<T>)`, surely they should have the same type!

- If labels are part of the type system…are two different function types compatible? Can `removeAll(equalTo:)` be passed to a parameter of type `fn(&mut Self, T)`? Probably! But what about `fn(&mut Self, value: T)`? Is this a new implicit conversion? Can you change `fn(x: f64, y: f64)` to `fn(y: f64, x: f64)`?

- What happens when you put `fn(value: isize)` into `dyn Any`? Can you get it out as `fn(isize)`? As `fn(offset: isize)`?

- Putting the labels in the type undermines the user model of “these functions have different names”. (I guess this only matters if you have name-based overloading, which I’m willing to call it. I’ve got some thoughts on Python-style labels without overloading but I’m still working them out.)

Now, there is a downside of Option A too: sometimes function pointers really _would_ benefit from labels. But those labels go with the name of the function pointer argument, not what gets passed to it. We had the idea in Swift that

```rust
fn for_each(
  &self,
  apply process: impl FnMut(key: K, value: V)
) {
  // Yes, this is inefficient, don’t worry about it.
  for k in self.keys() {
    process(key: k, value: self[k])
  }
}

```

would be sugar for something like

```rust
fn for_each(
  &self,
  apply process(key:value:): impl FnMut(K, V)
) {
  for k in self.keys() {
    process(key: k, value: self[k])
  }
}

```

but never got around to implementing it. (Still could some day, though; we left space for it.) It’s not strictly necessary, and since there are always functions without labels it’s not out-of-place or anything.

So yeah, don’t go with Option B.

[Previous page](https://internals.rust-lang.org/t/pre-rfc-named-arguments/16413.md?page=3)

[Next page](https://internals.rust-lang.org/t/pre-rfc-named-arguments/16413.md?page=5)
