Skip to content

Commit c630e04

Browse files
committed
Adding keyword arguments to Rust
1 parent 0026373 commit c630e04

File tree

1 file changed

+139
-0
lines changed

1 file changed

+139
-0
lines changed

active/0000-keyword-arguments.md

+139
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
- Start Date: 2015-01-26
2+
- RFC PR #: (leave this empty)
3+
- Rust Issue #: (leave this empty)
4+
5+
6+
# Summary
7+
8+
Add keyword arguments to Rust in a backwards-compatible way.
9+
This allows overloading that is safe in regards to type inference while being decided at compile time.
10+
11+
# Motivation
12+
13+
Allow another kind of argument in Rust. Current arguments distinguish themselves from other types by their order.
14+
If there is no semantic reason for a certain order, the argument order in functions is basically arbitrary.
15+
Consider a call like this:
16+
```rust
17+
window.addNewControl("Title", 20, 50, 100, 50, true);
18+
```
19+
20+
First of all, the call tells nothing the user about what those values mean.
21+
Secondly, their order is arbitrary and must be memorized. What I propose is that this call would rather look like:
22+
```rust
23+
window.addNewControl(
24+
title => "Title",
25+
xPosition => 20,
26+
yPosition => 50,
27+
width => 100,
28+
height => 50,
29+
drawingNow => true);
30+
```
31+
32+
While you might argue that this is more verbose, this is already the standard in the JavaScript ecosystem.
33+
A lot of libraries in JavaScript actually have a convention with calling with associative arrays like:
34+
```JavaScript
35+
window.addNewControl({ xPosition: 20, yPosition: 50, width: 100, height: 5,
36+
drawingNow: true });
37+
```
38+
If this wasn't a convenient pattern, nobody would bother to do it.
39+
40+
An additional benefit is that this leads to an easy implementation of optional parameters and optional parameters.
41+
In languages like PHP, default parameters must come at the end of the function and must have a value.
42+
43+
You can have optional parameters in the beginning, middle and end of functions without any default value.
44+
This means that this design allows for truly optional parameters without any run-time cost of using an `Option`.
45+
46+
# Detailed design
47+
Currently Rust has
48+
49+
```rust
50+
fn slice(&self, begin: usize, end: usize) -> &'a str
51+
fn slice_from(&self, begin: usize) -> &'a str
52+
fn slice_to(&self, end: usize) -> &'a str
53+
```
54+
55+
This can be changed to
56+
57+
```rust
58+
fn slice(&self, from => begin: usize, to => end: usize) -> &'a str
59+
fn slice(&self, from => begin: usize) -> &'a str
60+
fn slice(&self, to => end: usize) -> &'a str
61+
```
62+
63+
Note that these are three different functions that have three different signatures.
64+
The keywords a function accepts is part of its signature. You can call these functions like this:
65+
66+
```rust
67+
foo.slice(from => 5); //equivalent to current foo.slice_from(5)
68+
foo.slice(to => 9); //equivalent to current foo.slice_to(9)
69+
foo.slice(from => 5, to => 9); //equivalent to current foo.slice(5, 9)
70+
foo.slice(from => 9, to => 5); //equivalent to current foo.slice(5, 9)
71+
```
72+
73+
The trait looks like
74+
75+
```rust
76+
Fn(&self, from => begin: usize, to => end: usize) -> &'a str
77+
//which desugars to
78+
Fn<({from: usize, to: usize}, &Self), &'a str>
79+
```
80+
81+
So this is equivalent for using a `struct` to pass around your keyword parameters.
82+
But now you are able to use the same function name several times as long as the keywords are different.
83+
84+
# Drawbacks
85+
86+
This is a more complicated design than just having default arguments and overloading.
87+
Now there are both positional and keyword arguments and they interact with lifetimes, traits, and closures.
88+
89+
# Alternatives
90+
91+
A better design to the above function might be designing it like so:
92+
93+
```rust
94+
let title = "Title";
95+
let position = Position(20, 50);
96+
let dimensions = Dimension(100, 50);
97+
window.addNewControlDrawingNow(title, position, dimensions);
98+
```
99+
100+
Now the function takes three parameters, and we assigned them meaningful names.
101+
Instead of passing a boolean, we created functions that do different things.
102+
They could be named something like `addNewControl` and `addNewControlDrawingNow`.
103+
104+
While this design is better, it still doesn't solve the problem of having to remember of the order.
105+
Where do you put `dimensions`, `position`, and the `title`?
106+
At least the compiler will now verify that the types are correct.
107+
It is still up to the programmer to actually name those variables well, instead of the API specifying what the keywords should be.
108+
109+
If keyword arguments themselves are not implemented, then there's also the issue of overloading to enable better API design.
110+
Another proposal would be to assign keywords to all current Rust code with their local names.
111+
So something like:
112+
113+
```rust
114+
foo.slice(begin => 5, end => 10);
115+
```
116+
117+
This will solve the problem of the naming of parameters, but won't solve the problem of overloading.
118+
Because this design this allows you to call this method like:
119+
120+
```rust
121+
foo.slice(x, y);
122+
```
123+
124+
This means that you potentially can't infer the types of the arguments if they were overloaded.
125+
Another overload could add another `slice()` that takes a different type of parameter in the first slot.
126+
What type would `x` be then?
127+
Even though it has a different name, being able to call keyword arguments without keywords breaks overloading.
128+
129+
With mandatory keywords it is always known what type a certain keyword is.
130+
131+
# Unresolved questions
132+
133+
The trait desugaring is an addition based on the discussion on the internals board.
134+
Could it come first (before the self parameter) or does it have to come last?
135+
136+
Another possibility is a different syntax for keywords. Maybe it would be better to unify it with the struct syntax.
137+
So something like `foo.slice(begin: 5, end: 10)` because it desugars to a keyword struct anyway.
138+
139+
But what would the declaration look like?

0 commit comments

Comments
 (0)