# 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:** 6

<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:** [July 7, 2022, 1:57am UTC](https://internals.rust-lang.org/t/pre-rfc-named-arguments/16413/101 "2022-07-07T01:57:31Z")

</div>

> [@Ichoran](#):
>
> Both of these are incredibly boilerplatey at both the definition site and the use site. So this is a good argument _for_ named arguments. (Or improvements to boilerplate.)
> 
> If your argument was "nobody needs that stuff--it's technically possible but in practice it just doesn't come up", that'd be different. Then the feature would possibly be not pulling its weight

Your definition of boilerplate is very much different from mine or from the general consensus for that matter. Having strongly typed interfaces is good design, **not** redundant boilerplate. I see no redundancy in declaring a properly named struct to encapsulate multiple pieces of information that belong together, nor do I see a problem with making functions with long parameter lists be a little less convenient to encourage better design practices. Everything in programming is a tradeoff and if you want say prototyping without mucking about with defining types than perhaps rust is not the best tool for you. There are plenty of scripting languages that choose the opposite set of tradeoffs.

Moreover, yes, having multiple ways to do the same thing as you suggest has much higher overall costs.

---

<div class="post-metadata">

**Author:** ![Ichoran](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/ichoran/32/1780_2.png) [@Ichoran](https://internals.rust-lang.org/u/Ichoran)\
**Post date:** [July 7, 2022, 4:40am UTC](https://internals.rust-lang.org/t/pre-rfc-named-arguments/16413/102 "2022-07-07T04:40:43Z")

</div>

> [@CAD97](#):
>
> This isn't overloading/polymorphism; this is just method lookup (which _can_ be seen as related, since it's type-dispatched). These are still two different functions with two different names, `Vec::size` and `File::size`.

It's not just related, it's _isomorphic_. It's exactly the same operation!

Look:

```rust
// This is evil because "overloading"

fn print(x: i32) { unimplemented!(); }
fn print(x: String) { unimplemented!(); }
fn print(x: i32, y: String) { print(x); print(y) }

fn main() {
    let i = 1i32;
    let s = "salmon".to_string();
    
    // Same method name, postfix arg(s)
    print(i);
    print(s);
    print(i, s);
}

```

vs.

```rust
// This is brilliant because it's overloading BUT args appear on the LEFT!
// Or because it has tons of boilerplate that surely clarifies things?

struct NewI32(i32);
impl NewI32 {
    fn print(self) { unimplemented!(); }
}

struct NewString(String);
impl NewString {
    fn print(self) { unimplemented!(); }
}

struct NewBoth((i32, String));
impl NewBoth {
    fn print(self) {
        let NewBoth((i, s)) = self;
        NewI32(i).print();
        NewString(s).print();
    }
}

fn main() {
    let i = NewI32(1i32);
    let s = NewString("salmon".to_string());
    let is = NewBoth((2i32, "cod".to_string()));
    
    // Same method names, prefix arg
    i.print();
    s.print();
    is.print();
}

```

There's no ad-hoc polymorphism, no dynamic dispatch. It's all static dispatch both ways, except one way is a truckload of work to do on purpose (but happens inadvertently all the time as people create libraries and use the same names), and the other way is maligned because...doing it on purpose is worse than doing it by accident?

Java (and C++ and Scala and...) also dispatches statically to _overloaded_ methods based on the static type of the arguments. (Clojure's multimethods and Julia's multiple dispatch _do_ pick based on dynamic type. That _is_ ad-hoc polymorphism. Plus lots of languages have ordinary polymorphism on the first argument of the function--but again this is a different kettle of fish.)

So, anyway, Rust totally has overloading (but only for the prefix argument).

---

<div class="post-metadata">

**Author:** ![Ichoran](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/ichoran/32/1780_2.png) [@Ichoran](https://internals.rust-lang.org/u/Ichoran)\
**Post date:** [July 7, 2022, 4:49am UTC](https://internals.rust-lang.org/t/pre-rfc-named-arguments/16413/103 "2022-07-07T04:49:17Z")

</div>

> [@anon32976453](#):
>
> I see no redundancy in declaring a properly named struct to encapsulate multiple pieces of information that belong together, nor do I see a problem with making functions with long parameter lists be a little less convenient to encourage better design practices.

I'm not sure why you don't see redundancy or boilerplate in something that is literally the same thing twice and/or literally the same thing only longer and with an extra superfluous name (for many use cases).

But, anyway, sure, if you don't find it redundant or boilerplatey, that's great for you.

However, if I'm not going to question that you _don't_, you can't question that I _do_.

And regarding general consensus--well, do you have a link to a poll or a discussion involving a sufficiently broad group of people to be somewhat representative?

---

<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:** [July 7, 2022, 6:06am UTC](https://internals.rust-lang.org/t/pre-rfc-named-arguments/16413/104 "2022-07-07T06:06:51Z")

</div>

> [@Ichoran](#):
>
> > [@anon32976453](#):
> >
> > I see no redundancy in declaring a properly named struct to encapsulate multiple pieces of information that belong together, nor do I see a problem with making functions with long parameter lists be a little less convenient to encourage better design practices.
> 
> I'm not sure why you don't see redundancy or boilerplate in something that is literally the same thing twice and/or literally the same thing only longer and with an extra superfluous name (for many use cases).

They didn't claim to not see boilerplate, only not see redundancy.

There is no extra redundancy involved in

```rust
struct foo_Args {
    pub x: u32,
    pub y: u32,
    pub z: u32,
}
fn foo(foo_Args { x, y, z }: foo_Args);

foo(foo_Args {
    x: 123,
    y: 456,
    z: 789,
});

```

compared to with named arguments

```rust
fn foo(pub x: u32, pub y: u32, pub z: u32);

foo(
    x = 123,
    y = 456,
    z = 789,
);

```

and in fact, there is merely an overhead of _9 + 2n_ tokens\[1\] at the definition site and a constant overhead of _just 3_ tokens at the call site. And that's with the _most_ generous named argument syntax.

A more reasonably addable syntax (and one I do think is a good idea) is to treat this as sugar for the full struct, and answers all the gnarly questions about names in function types as "it's a single argument of an unnameable type."

```rust
fn foo(struct { x: u32, y: u32, z: u32 });

foo(struct {
    x: 123,
    y: 456,
    z: 789,
});

```

> **because I like writing them, a macro**
>
> ```rust
> macro_rules! named_args {
> {
> // TODO: fudge some mostly correct generics support
> $vis:vis fn $fn:ident (
> $($unnamed:ident: $UnnamedTy:ty,)*
> // FIXME: use pub introducer; for some reason pub matches as $:ident
> $(@ $named:ident: $NamedTy:ty),+ $(,)?
> ) $(-> $RetTy:ty)? { $($body:tt)* }
> } => { ::paste::paste! {
> #[allow(non_camel_case_types)]
> $vis struct $fn {
> $(pub $named: $NamedTy,)*
> }
> $vis fn $fn(
> $($unnamed: $UnnamedTy,)*
> $fn { $($named),* }: $fn
> ) $(-> $RetTy)? { $($body)* }
> #[macro_export] // FIXME: use a real pub_macro feature polyfill
> macro_rules! [<__$fn>] {
> (
> $($$$unnamed:expr,)*
> $($named: $$$named:expr),* $$(,)?
> ) => (
> $fn(
> $($$$unnamed,)*
> $fn { $($named: $$$named),* }
> )
> );
> }
> $vis use [<__$fn>] as $fn; 
> }};
> }
> 
> mod example {
> named_args! {
> pub fn foo(x: i32, @ y: i32, @ z: i32) {
> dbg!(x, y, z);
> }
> }
> }
> 
> // FIXME: cannot call macro by path, must use; fixed with $self
> use example::foo;
> 
> fn main() {
> foo!(123, y: 456, z: 789);
> }
> 
> ```

> [@Ichoran](#):
>
> However, if I'm not going to question that you _don't_, you can't question that I _do_.

I don't think that's how burden of proof works.

> [@Ichoran](#):
>
> extra superfluous name

1. It's not superfluous as soon as _anyone_ stores/encapsulates an instance of it.
2. If you want to be clever, just name it the same as the function.

> [@Ichoran](#):
>
> Java (and C++ and Scala and...) also dispatches statically to _overloaded_ methods based on the static type of the arguments.

That I didn't remember this was the case isn't a good sign though, tbh... I vaguely remember this being listed as a Java gotcha (that it _didn't_ do dynamic type dispatch). Though, how does it interact with generics/templates/generics/etc? (C++ I know calling an overloaded function in a template will call the instantiated type; I _think_ Java chooses the overload with the used generic base class, and this is where the gotcha is that it _isn't_ dispatched based on instantiated type.) With Rust you don't have access to inherent methods on generic types, so you don't have to define what happens in this case.

> [@Ichoran](#):
>
> So, anyway, Rust totally has overloading (but only for the prefix argument).

There is still a difference, though, in that the syntax does provide a meaningful communication channel; Rust still doesn't have function overloading on the type of the first argument (e.g. `print(i)`), _only on the method receiver_ (e.g. `i.print()`). (And due to this difference I still hold that method lookup is _meaningfully_ different from overloading.)

The method receiver is set apart and also subject to a bunch of other rules, such as autoref. The method receiver is set apart because it is treated specially.

(They're also subtly different at a compiler level; method resolution is done by resolving the type of the reciever and then doing method lookup on the name; overloading first resolves the name to the overload set and then selects the appropriate overload based on the argument types. The order _does_ matter; e.g. as a function argument the argument recieves type constraints on an inference variable, whereas the type must be completely known before method resolution.)

Additionally, the _really gnarly_ cases of type-based method lookup come when resolving overloads based on multiple arguments rather than just the type of the one (method reciver). (_Especially_ when types don't have to match precisely, because some overloads take a polymorphic type.)

Rust also has strong type inference that makes the types of bindings not necessarily known immediately. It's this multi-type-variable function resolution which gets expensive fast.

* * *

1. counting: `struct`, `foo_Args`, `{`, (function args,) `}`; `foo_Args`, `{`, (function args,), `}`, `:`, `foo_Args`. The _2n_ factor is for the commas in the struct def and the field names in the argument destructure, with the latter being the only thing you could really call redundant.

---

<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:** [July 7, 2022, 6:24am UTC](https://internals.rust-lang.org/t/pre-rfc-named-arguments/16413/105 "2022-07-07T06:24:36Z")

</div>

Check out the latest rust survey where one of the top concerns ranked by users is the complexity of Rust, an already quite a large language. Dumping a shedload of complexity to duplicate **existing functionality** with a different syntax is therefore a non starter.

As I said, I do not find declaring additional types to be "boilerplate" - that's a core aspect of a statically typed language. I have said above, that existing patterns can be improved upon by filling obvious gaps in the existing syntax - for example, adding inference for struct literals' names. That has negligible complexity costs whereas having multiple ways of doing the same thing is very costly on learnability, maintenance and ability to reason about code.

Lastly, let's touch on the equivalence fallacy: Where I am arguing against adding redundant complexity to Rust, the so called negatively affected users are the subset of users who advocate for named arguments only. Where you argue for adding names arguments, the affected users will be **all rust users**. Even if I don't want this complexity in my code, the reality is I would still need to deal with it if it exists in the language. Training would still need to account for this. Using of external APIs would still be affected by other people's choices. The other option is fragmentation of the ecosystem into different language subsets.

Therefore, the two arguments are not equivalent.

---

<div class="post-metadata">

**Author:** ![lordan](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/lordan/32/2107_2.png) [@lordan](https://internals.rust-lang.org/u/lordan)\
**Post date:** [July 7, 2022, 2:39pm UTC](https://internals.rust-lang.org/t/pre-rfc-named-arguments/16413/106 "2022-07-07T14:39:38Z")

</div>

> [@CAD97](#):
>
> Rust still doesn't have function overloading on the type of the first argument (e.g. `print(i)`), _only on the method receiver_ (e.g. `i.print()`).

At least on Nightly you _can_ implement overloading by implementing `Fn/Mut/Once`:

```rust
#![feature(unboxed_closures)]
#![feature(fn_traits)]

#[derive(Clone, Copy)]
struct S;

impl FnOnce<(u8,)> for S {
    type Output = u8;
    extern "rust-call" fn call_once(self, args: (u8,)) -> Self::Output {
        args.0
    }
}

impl FnOnce<(&'static str,)> for S {
    type Output = &'static str;
    extern "rust-call" fn call_once(self, args: (&'static str,)) -> Self::Output {
        args.0
    }
}

fn main() {
    let s = S;
    println!("{}", s(8u8)); // prints "8"
    println!("{}", s(99)); // ok if there is no ambiguity, prints "99"
    println!("{}", s("Hiya!")); // prints "Hiya!"
}

```

You can also effectively have function overloading based on the return type, e.g., with `Into::into`.

---

<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:** [July 7, 2022, 5:33pm UTC](https://internals.rust-lang.org/t/pre-rfc-named-arguments/16413/107 "2022-07-07T17:33:30Z")

</div>

> [@lordan](#):
>
> At least on Nightly you _can_ implement overloading by implementing `Fn/Mut/Once`:

Yes, this is effective overloading. But current track is not to ever stabilize this ability.

> [@lordan](#):
>
> You can also effectively have function overloading based on the return type,

This falls under trait-dispatched parametric polymorphism.

* * *

Ultimately: I agree that the difference between type-driven adhoc overload sets and the current method lookup and trait dispatch is _slight_, but what I'm really arguing is that there _is_ a _well-defined_ difference.

The difference is pretty much exclusively in that overload sets are adhoc. Trait lookup is parametric, and method resolution is constrained. (E.g. a difference is that due to how Java namespacing works, all items in an overload set must be declared in the same file. Rust's module system would allow constructing an adhoc overload set by importing the name from multiple files/modules, as well as parts of the overload set having different visibility, etc. There are real differences between adhoc overload sets and method lookup.)

I would like to explicitly disclaim any opinion, expressed or implied, on whether this side of the line is "better."

---

<div class="post-metadata">

**Author:** ![Ichoran](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/ichoran/32/1780_2.png) [@Ichoran](https://internals.rust-lang.org/u/Ichoran)\
**Post date:** [July 7, 2022, 5:55pm UTC](https://internals.rust-lang.org/t/pre-rfc-named-arguments/16413/108 "2022-07-07T17:55:42Z")

</div>

> [@anon32976453](#):
>
> Where you argue for adding names arguments, the affected users will be **all rust users**.

Yes, that _is_ a fair point. I personally find that named arguments, even though they increase formal complexity, actually _reduce_ perceived complexity because code using them often places reduced cognitive demands on the reader (and writer). It _feels_ less complex. So I don't fully buy the complexity argument. But you're absolutely right that it _does_ affect everyone. There's no getting around it. And there's a good argument to make that not complexity per se but "I've got this but just barely--don't make me change anything!" is a very valid reason to not make any changes unless they _really_ pay for themselves.

So, yes, you're right: the two arguments are not equivalent.

---

<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:** [July 7, 2022, 6:05pm UTC](https://internals.rust-lang.org/t/pre-rfc-named-arguments/16413/109 "2022-07-07T18:05:14Z")

</div>

> [@CAD97](#):
>
> But current track is not to ever stabilize this ability.

I very much hope we do stabilize it. To the best of my knowledge, the main blocker has been the "rust-call" calling convention, which we could either stabilize, or replace with type-level variadics.

---

<div class="post-metadata">

**Author:** ![Ichoran](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/ichoran/32/1780_2.png) [@Ichoran](https://internals.rust-lang.org/u/Ichoran)\
**Post date:** [July 7, 2022, 6:23pm UTC](https://internals.rust-lang.org/t/pre-rfc-named-arguments/16413/110 "2022-07-07T18:23:58Z")

</div>

> [@CAD97](#):
>
> Rust still doesn't have function overloading on the type of the first argument (e.g. `print(i)`), _only on the method receiver_ (e.g. `i.print()`). (And due to this difference I still hold that method lookup is _meaningfully_ different from overloading.)
> 
> The method receiver is set apart and also subject to a bunch of other rules, such as autoref. The method receiver is set apart because it is treated specially.
> 
> (They're also subtly different at a compiler level; method resolution is done by resolving the type of the reciever and then doing method lookup on the name; overloading first resolves the name to the overload set and then selects the appropriate overload based on the argument types. [...])

Okay, but this is like saying that we have different rules for chick peas and garbanzo beans. Nothing prevents adoption of exactly the same rules for static dispatch for cases that are distinguished only by a trivial syntactic rewriting. Every `foo(x, y)` is equivalent to a `x.foo(y)` and `(x,y).foo()`.

It's all just dispatch to overloaded names. It's totally reasonable for type inference to step down a notch when you use overloaded names. (Would be good to have convenient syntax for type ascription, though.)

I agree that there can be all kinds of gnarly problems if you want to solve the most generic case possible. Same deal if your "method receiver" (chick pea) has complex types that would only be disambiguated by the method called. Maybe method receiver position is a good way to signal different expectations about type inference as compared to first argument (garbanzo bean). But what we _shouldn't_ maintain is that Rust has no overloading. We might argue that it already has exactly the right amount (I'm skeptical, but hey, it's a coherent position), but not that it doesn't have it because we chose to call the exact same thing by a different name.

(There are gotchas with Java regarding generics, if you forget that overloading _is not ad-hoc polymorphism_, so in the generic context you will statically dispatch to whichever overloaded method is most specific for _the root class in the hierarchy of all allowed generics_. In contrast, the multimethod approach (or dynamic dispatch) that Julia uses will use the actual type.)

(Cool macro by the way! I might actually something like that if it were idiomatic. As it is, I think it'd really raise the difficulty of someone else who needed to understand my code, e.g. me in a few months/years.)

---

<div class="post-metadata">

**Author:** ![Ichoran](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/ichoran/32/1780_2.png) [@Ichoran](https://internals.rust-lang.org/u/Ichoran)\
**Post date:** [July 7, 2022, 6:29pm UTC](https://internals.rust-lang.org/t/pre-rfc-named-arguments/16413/111 "2022-07-07T18:29:44Z")

</div>

> [@CAD97](#):
>
> A more reasonably addable syntax (and one I do think is a good idea) is to treat this as sugar for the full struct, and answers all the gnarly questions about names in function types as "it's a single argument of an unnameable type."
> 
> ```rust
> fn foo(struct { x: u32, y: u32, z: u32 });
> 
> foo(struct {
> x: 123,
> y: 456,
> z: 789,
> });
> 
> ```

Sure, an evolution in this direction could fulfill the need for named arguments adequately. Not sure this is quite enough, but it's a lot better than the existing case. I'd have to use it for a while to know whether it scratched enough of the itch.

I'm all for minimal evolution of existing features to meet the need rather than importing wholesale the implementations chosen by other languages.

---

<div class="post-metadata">

**Author:** ![scottmcm](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/scottmcm/32/2355_2.png) [@scottmcm](https://internals.rust-lang.org/u/scottmcm)\
**Post date:** [July 7, 2022, 7:24pm UTC](https://internals.rust-lang.org/t/pre-rfc-named-arguments/16413/112 "2022-07-07T19:24:29Z")

</div>

> [@Ichoran](#):
>
> It's all just dispatch to overloaded names.

It's not the same, because you have to decide when you're resolving those names. See _Two-phase Lookup_, _Koenig Lookup_ in C++ for the kinds of horrible things that end up happening with ad-hoc overloading.

Putting the traits in the middle makes a huge difference. See [Justification for Rust not Supporting Function Overloading (directly) - #3 by scottmcm](https://internals.rust-lang.org/t/justification-for-rust-not-supporting-function-overloading-directly/7012/3)

---

<div class="post-metadata">

**Author:** ![Ichoran](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/ichoran/32/1780_2.png) [@Ichoran](https://internals.rust-lang.org/u/Ichoran)\
**Post date:** [July 7, 2022, 9:00pm UTC](https://internals.rust-lang.org/t/pre-rfc-named-arguments/16413/113 "2022-07-07T21:00:56Z")

</div>

> [@scottmcm](#):
>
> It's not the same, because you have to decide when you're resolving those names.

It's entirely possible to make the same decision in both cases.

You just can't win an argument that two situations isomorphic up to a trivial syntactic rearrangement have any huge showstoppers in one case vs. the other.

You might be able to argue that the different syntax sufficiently strongly suggests different expectations that it is unwise to use the same rules for both. But you can't argue that it's _necessarily_ fundamentally different. It's isomorphic!

---

<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:** [July 8, 2022, 1:23am UTC](https://internals.rust-lang.org/t/pre-rfc-named-arguments/16413/114 "2022-07-08T01:23:03Z")

</div>

> [@Ichoran](#):
>
> there's a good argument to make that not complexity per se but "I've got this but just barely--don't make me change anything!" is a very valid reason to not make any changes unless they _really_ pay for themselves.

Painting people who disagree with you as stupid doesn't earn your argument any points, not does it help to disregard valid criticisms since you yourself can't see them personally.

While in your own personal project you could dismiss this as inconsequential, in a large code base worked on by multiple people over time even the smallest duplication of features becomes a sink in productivity over time and a source of complexity and pain. If we want rust to become the language for the next 50 years we need to cater for such code bases! Google has over a **billion** LOC. What kind of code style would you reckon they prefer? They specifically disallowed a large chunk of advanced features of C++ which "reduce boilerplate" because they preferred to have (more) code that is easier to reason about and that allows to onboard new engineers faster.

**Every feature added to Rust** must satisfy the condition that it **really** pays for itself. That ought to be an obvious fundamental requirement for that same objective.

Edit: As an example of this, at my $job we maintain a large code base in C++. All the engineers are fully capable and understand C++, yet we still waste time on endless debates because different people have differing preferences. So yes, the complexity of C++ is a major problem even for experts and Rust should do better.

---

<div class="post-metadata">

**Author:** ![toc](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/toc/32/6692_2.png) [@toc](https://internals.rust-lang.org/u/toc)\
**Post date:** [July 8, 2022, 6:35am UTC](https://internals.rust-lang.org/t/pre-rfc-named-arguments/16413/115 "2022-07-08T06:35:39Z")

</div>

> [@scottmcm](#):
>
> It's not the same, because you have to decide when you're resolving those names. See _Two-phase Lookup_, _Koenig Lookup_ in C++ for the kinds of horrible things that end up happening with ad-hoc overloading.

While it depends on some other features in the pipeline, it seems reasonable to me that rust could add more overloading later along the lines of

```rust
trait A {}
trait B {}
impl !B for A;
impl !A for B;

// Now this is fine:
fn foo(a: impl A) { ... }
fn foo(b: impl B) { ... }

```

---

<div class="post-metadata">

**Author:** ![Ichoran](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/ichoran/32/1780_2.png) [@Ichoran](https://internals.rust-lang.org/u/Ichoran)\
**Post date:** [July 8, 2022, 10:41pm UTC](https://internals.rust-lang.org/t/pre-rfc-named-arguments/16413/116 "2022-07-08T22:41:36Z")

</div>

> [@anon32976453](#):
>
> While in your own personal project you could dismiss this as inconsequential, in a large code base worked on by multiple people over time even the smallest duplication of features becomes a sink in productivity over time and a source of complexity and pain.

But you're arguing against a feature that is universally used to simplify and clarify code, making it easier for people to understand each others' code especially in large projects. Unless people who have learned the language are already overwhelmed and can't take any more, adding simplifying and clarifying features _decreases complexity and pain_.

Rust _isn't_ an easy language to master. "I've got this but don't make me change" isn't any more of a charge of stupidity than is your "easier to reason about" argument.

But code with named arguments _is easier to reason about_!

There may be reasons why it's not possible to get there in a practical way (or why getting partway there is better because e.g. it fits better with the rest of the language). That happens a lot with languages--feature X is great but it just doesn't mesh with language Y's other syntax/features.

If you _disagree_ that named arguments makes code easier to reason about, then what is your opinion on Rust's existing struct syntax requiring names?

---

<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:** [July 9, 2022, 5:26am UTC](https://internals.rust-lang.org/t/pre-rfc-named-arguments/16413/118 "2022-07-09T05:26:06Z")

</div>

Counterargument: Swift. Literally _all_ of Apple SDK APIs use named arguments.

Named arguments _from the beginning_ (and crucially, ones that are _required_) are _objectively_ a useful tool for API design. There are edge cases to consider that do catch people out (e.g. function types and closures) but combined with a helpful compiler and an API design language that universally uses function argument labels, they serve as an enormous boon for quickly digesting an API surface.

Function argument labels are so important that even in languages that don't use them on calls, documentation typically shows them anyway. Rust trait functions used to be declarable without argument labels, but that usage is deprecated now. Public visible function argument labels are objectively a good thing.

Adding argument labels to an existing language is problematic because using argument labels leads to a drastically different API design language. _Implicitly optional_ argument labels are a _terrible_ idea, _especially_ when argument names weren't an API guarantee previously, because they're so easy to accidentally make a poor name part of the stable API.

But there is nothing _intrinsically bad_ about function calls using argument labels in a static language, (so long as they are a controlled part of a library's API,) because having them is no different API/ABI-wise than putting them in the function stem.

---

<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:** [July 9, 2022, 8:52am UTC](https://internals.rust-lang.org/t/pre-rfc-named-arguments/16413/119 "2022-07-09T08:52:54Z")

</div>

Swift is not a good counterargument at all: Swift has to be compatible to the prexisting platform APIs from Objective-C which was essentially a reimplantation of Smalltalk on top of C syntax. Smalltalk is a dynamically typed language and Objective-C leaned heavily on the same design and semantics.

I also did not say that naming parameters properly is a bad thing or that named arguments are always absolutely evil 🤦. I'm agreeing with most everything you said - Rust has already a naming scheme and we should not have two. Named arguments simply do not fit with the tradeoffs already made by Rust. They fit Swift due to its history. They fit python and other dynamic languages very well.

Lastly, while I agree there is a minor edge case where named arguments results in better API design (e.g having a non commutative function with two parameters of the same type) I still stand by my previous assertion that more generally, named arguments are somewhat inferior overall compared to relying on types because the ordering of parameters becomes more of an issue. Smalltalk has somewhat redundant APIs due to this. `IfTrue:IfFalse:` vs. `IfFalse:IfTrue:` (the concatenated names of the parameters form the function name in Smalltalk)

I've done some coding with Smalltalk-80 in the past and this was my experience. I still prefer to have traditional functions with few parameters (say 3-4 tops) and at $job we have a linter that checks exactly that. When adding overloading to the mix, the costs of named arguments greatly outweigh the benefits. Though even without it, you still have other costs such as having multiple ways to name the same thing. Also as I said before, making this a bit salty encourages splitting up functions so they won't have many parameters which results in better designed code overall.

We're literally going in circles here as I'm repeating myself from up-thread..

---

<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:** [July 9, 2022, 3:44pm UTC](https://internals.rust-lang.org/t/pre-rfc-named-arguments/16413/120 "2022-07-09T15:44:55Z")

</div>

> [@anon32976453](#):
>
> Swift has to be compatible to the prexisting platform APIs

This is demonstrably false.

Swift needs to be able to _call_ preexisting platform APIs. It does not need to _and in fact doesn't_ call them by their existing names. It can _and does_ include glue to rename _every single preexisting API_ from the existing API design language into the new one.

That Apple was able to do this alongside all of the other impossible things Swift does (e.g. a stable ABI in the face of library evolution) is a marvel of the amount of developer time they threw at the problem.

Swift/ObjC is a bindgen+annotations problem. Swift could have abandoned named argument labels in the transition (and would have; Apple was not afraid to make sweeping breaking changes early on, and _did_ make fundamental changes both to how they worked and how ObjC was mapped) if they were a poor fit for a static language.

---

<div class="post-metadata">

**Author:** ![bjorn3](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/bjorn3/32/2736_2.png) [@bjorn3](https://internals.rust-lang.org/u/bjorn3)\
**Post date:** [July 9, 2022, 5:01pm UTC](https://internals.rust-lang.org/t/pre-rfc-named-arguments/16413/121 "2022-07-09T17:01:25Z")

</div>

> [@CAD97](#):
>
> It can _and does_ include glue to rename _every single preexisting API_ from the existing API design language into the new one.

It does so using a consistent pattern though AFAIK. If swift didn't support named arguments, the function names themself would need to include the argument names to avoid conflicts between for example `-[NSString initWithFormat:arguments:]`, `-[NSString initWithFormats:locale:]` and `-[NSString initWithFormats:locale:arguments:]`.

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

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