Skip to content

Commit 8b72b4b

Browse files
committed
First version, ready to publish
1 parent 8490e0e commit 8b72b4b

File tree

1 file changed

+210
-0
lines changed

1 file changed

+210
-0
lines changed

active/0000-default-arguments.md

+210
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
- Start Date: 2014-06-14
2+
- RFC PR #: (leave this empty)
3+
- Rust Issue #: (leave this empty)
4+
5+
# Summary
6+
7+
Add the support for default function arguments and named function arguments in Rust.
8+
9+
# Motivation
10+
11+
There have been demand for a long time for default and named arguments in Rust. They cover various use cases:
12+
13+
* API design. Some functions in the Rust standard distribution libraries or in third party libraries have many similar methods that only vary by the presence of an additional parameter. Having to find and to memorize different names for them can be cumbersome.
14+
15+
* If this feature can be added to Rust, it can't be added post-1.0, as a lot of functions in the Rust standard library must be rewritten in order to benefit this feature.
16+
17+
* Foreign bindings. This has been discussed on the mailing list recently about Qt5 bindings. Binding functions that make heavy use of default arguments or operator overloading can be very messy with the current system.
18+
19+
# Detailed design
20+
21+
## Split example
22+
23+
Instead of:
24+
25+
```rust
26+
fn split(s: &str, sep: char) { ... }
27+
fn splitn(s: &str, sep: char, count: uint) { ... }
28+
29+
split("hello world", ' ');
30+
splitn("hello world", ' ', 2);
31+
```
32+
33+
We can have:
34+
35+
```rust
36+
fn split(s: &str, sep: char = ' ', count: Option<uint> = None) { ... }
37+
38+
split("hello world"); // -> split("hello world", ' ', None)
39+
split("hello,world", sep: ','); // -> split("hello,world", ',', None)
40+
split("hello world 42", count: Some(2)); // -> split("hello world", ' ', Some(2))
41+
```
42+
43+
## Practical design
44+
45+
> I'll refer to function/closure/proc declaration/call as "function declaration/call" in order to avoid repetitions.
46+
47+
### Function declaration
48+
49+
In function declaration, you can specify a default value for arguments.
50+
51+
The default value must be a static constant and having the same type as the argument.
52+
53+
All arguments can have a default value or not, not only the last arguments (as in C++). As any other function, the arguments without default value must be set, either by setting previous argument or by setting it with named argument.
54+
55+
```
56+
fn_args ::= fn_arg (',' fn_args)? ;
57+
fn_arg ::= ident arg_type? default_value? ;
58+
arg_type ::= ':' type ;
59+
default_value ::= '=' static_expr ;
60+
```
61+
62+
`Example:`
63+
64+
```rust
65+
fn toto(a: uint, b: uint) { ... }
66+
fn toto(a: uint, b: uint = 5) { ... }
67+
fn toto(a: uint, b: uint = 5, c: uint) { ... }
68+
// ~~~~~^~~~~~~
69+
// This argument MUST be set at call-time
70+
// Either by setting previous argument, or
71+
// by setting this argument with named arg syntax.
72+
73+
let f = |a| ...;
74+
let f = |a, b = 1u| ...;
75+
let f = |a, b: uint = 1| ...;
76+
77+
let f = proc(a: uint) { ... };
78+
let f = proc(a: uint, b: uint = 1) { ... };
79+
```
80+
81+
### Method declaration
82+
83+
When implementing a trait method using default values for arguments, you don't have to rewrite them.
84+
85+
If you do, it will result in a compilation error.
86+
87+
```rust
88+
trait World {
89+
fn split(&self, count: uint = 2);
90+
}
91+
92+
struct MyWorld;
93+
94+
impl World for MyWorld {
95+
fn split(&self, count: uint) {
96+
// Split the world...
97+
}
98+
}
99+
100+
struct BadWorld;
101+
102+
impl World for BadWorld {
103+
fn split(&self, count: uint = 3) {
104+
// Compile-time error, as you redefine the default value.
105+
// Note that this will result in an error even if the default value
106+
// indicated here is the same that the one present in trait definition.
107+
}
108+
}
109+
```
110+
111+
### Function call
112+
113+
In function call, you can omit arguments with default value as if the function doesn't have these arguments, they will be added at compile-time.
114+
115+
> Note that you MUST set arguments which don't have default values.
116+
117+
You can set specific arguments by naming it and set a value with a `<name>: <value>` syntax, even those without default value.
118+
119+
```
120+
call_args ::= call_arg (',' call_args)? ;
121+
call_arg ::= expr_arg | named_arg ;
122+
expr_arg ::= expr ;
123+
named_arg ::= ident ':' expr ;
124+
```
125+
126+
`Example:`
127+
128+
```rust
129+
fn toto(a: uint) { ... }
130+
toto(); // Compile-time error
131+
toto(1);
132+
133+
fn toto(a: uint = 1) { ... }
134+
toto(); // Resolved as toto(1)
135+
toto(2);
136+
137+
fn toto(a: uint, b: uint = 2) { ... }
138+
toto(1); // Resolved as toto(1, 2)
139+
toto(1, 3);
140+
toto(1, b: 5); // Resolved as toto(1, 5)
141+
toto(b: 6, a: 2); // Resolved as toto(2, 6)
142+
143+
fn toto(a: uint, b: uint = 2, c: uint) { ... }
144+
toto(1); // Throw an error, because c is not set here.
145+
toto(1, 2, 3); // We set `b` so we can set `c` too.
146+
toto(1, c: 22); // Resolved as toto(1, 2, 22)
147+
toto(1, c: 22, 3); // Resolved as toto(1, 3, 22)
148+
// Same as std::fmt format string definition.
149+
toto(b: 123, c: 456, a: 789); // Resolved as toto(789, 123, 456)
150+
151+
fn create_window(title: &str = "My window", size: (uint, uint) = (800, 600) { ... }
152+
create_window(); // Resolved as create_window("My window", (800, 600))
153+
create_window(size: (1024, 768)); // Resolved as create_window("My window", (1024, 768)
154+
155+
fn split_str(s: &str, sep: char = ' ') { ... }
156+
split_str("hello world"); // Resolved as split_str("hello world", ' ')
157+
split_str("hello,world", sep: ','); // Resolved as split_str("hello,world", ',')
158+
```
159+
160+
161+
## Function signature
162+
163+
The named arguments feature modify the way functions signature is determined, as the ABI shouldn't be changed a lot when modifying function declaration.
164+
165+
The function signature change when:
166+
167+
- Modifying arguments order, as before.
168+
- Modifying arguments names, because of named arguments feature (as we have to keep arguments names in function signature)
169+
- Modifying arguments default values, because of default arguments features (as we have to keep the default value in function signature)
170+
171+
The function ABI change only when modifying arguments order, as the arguments names and default values are "lost" when the function call is compiled.
172+
173+
# Drawbacks
174+
175+
Some people have shown reticence to include those features in the language. Having different names for functions that have different signatures can improve readability.
176+
177+
Default and named arguments are maybe not enough to solve the binding problem, especially for libraries that use a lot of function overloading (? not sure about this).
178+
179+
# Alternatives
180+
181+
* Don't do this.
182+
* Add only keyword arguments, or only default arguments.
183+
184+
# Unresolved questions
185+
186+
* Should we accept only last-arguments for default/named arguments, like C++ do?
187+
This could forbid the following syntax:
188+
```rust
189+
fn toto(a: uint, b: uint = 2, c: uint) { ... }
190+
```
191+
* Should we accept non-static expression (limited to declaration scope) in default argument value?
192+
This could allow the following syntax:
193+
```rust
194+
fn slice(&self, start: uint = 0, end: uint = self.len()) { ... }
195+
// Allowed, self is in declaration-scope
196+
197+
fn slice(&self, start: uint = 0, end: uint = toto.len()) { ... }
198+
// Forbidden, toto is not in declaration-scope
199+
```
200+
201+
* Should we "sugarize" `Option`-typed arguments, like:
202+
```rust
203+
fn split(s: &str, sep: char = ' ', count: Option<uint> = None) { ... }
204+
205+
split("hello world"); // -> split("hello world", ' ', None)
206+
split("hello world 42", count: 2); // -> split("hello world 42", ' ', Some(2))
207+
```
208+
> Note that this "sugar" could be generalized for all `Option`-typed values, but it's not the point here.
209+
210+
* For method declaration/implementation, why not letting the implementer struct defining default values, and leave the trait method definition with an "optional" argument (with a special syntax)

0 commit comments

Comments
 (0)