One idea to support overloading is to add similar sugar to enums.
Enum variants can not have defaults, because they use the same syntax as for struct tuples.
Functions, that are overloaded, can be called with named argument syntax as long at least one of the names is not an argument to the other overloaded functions.
All enum variants except one must take parameters, where the parameters are of different type signatures.
The one without parameters represents the unit type ().
There must exist one overloaded function for each enum.
It seems necessary to allow a struct tuple constructor on structs if we implement overloading. Here is the reason:
When refactoring an overloaded function that one wants to call with named parameters, one can combine an enum that wraps a structure, for example:
slice(&self, from: uint = 0, to: uint = self.len()) { ... }
slice(&self) { self.slice(..) }
// Works with overloading and named argument syntax
foo.slice();
foo.slice(0, 10);
foo.slice(start: 0, ..);
When we refactor, we have to pick either unnamed or named argument syntax, because struct tuples only support unnamed while structs only support named.
struct Range { from: Option<uint>, to: Option<uint> }
fn slice(&self, Some(Range { from = 0, to = self.len() }) { ... }
fn slice(&self, None) { ... }
foo.slice(); // Works as before
foo.slice(0, 10); // ERROR: This function uses named arguments
// Desugars into Some(Range { from: Some(0), to: None })
foo.slice(start: 0, ..);
We need the tuple constructor for structs to avoid error. This could be implemented manually by writing a function Foo that constructs Foo.
One alternative is to require named calling syntax on overloaded functions. This will force the programmer to refactor into structs instead of struct tuples.
It might be possible to allow overloading with a mixture of enum variants and function arguments. It works when enum variants have different type signatures and the signatures of normal functions are treated separately from the enums.
The tuple sugar can be an implicit cast into the struct, but only if all members of the struct are public. The same goes for implicit cast into the tuple struct.
Using structs to fake keyword arguments is a giant hack (it requires unergonomic declaration and use of an argument struct). Iād rather keep using a macro for that use case even if this sugar was implemented.
I don't know what you're referring to. The totality of your comments deal with the call site, while I am talking about using struct declaration and use. Any struct syntax sugar will require this preamble and usage:
struct FuncArgs {
arg1: int = 1,
arg2: int = 2,
arg3: int = 3,
}
fn func(args: FuncArgs) -> int {
args.arg1 + args.arg2 + args.arg3
}
vs
fn func(arg1: int = 1, arg2: int = 2, arg3: int = 3) -> int {
arg1 + arg2 + arg3
}
One looks like a proper feature, and one looks like a hack.
What I like in this proposal is that itās not adding any features to Rust except of sugar (which you donāt have to use) that compiles to currently legal Rust code. It is kind of hack, butitās not uncommon to pass a structure as an argument, when they are complicated enough. Another advantage of this approach (in comparison to keyword arguments as a language feature) is that itās always simply a struct: itās obvious when itās evaluated and how it is passed to callee; and you enable caller to store that struct anywhere and pass it as a single value.
Unergonomic declaration isnāt that bad, because you delcare function only once, but it can be sugared too, or at least wrapped behind a simple macro too, so I donāt think is a problem.
One think I donāt agree with @bvssvni is making None special. While it may save few keystrokes, i think it adds really a lot complexity and itās not obvious.
I guess that not really, you would either have to call foo((1,2)) or introduce ambiguity. Moreover, it would require introducing either implicit casts from tuples or fallback to tuples, for that to work:
let arg = (1,2); // treat it as truct literal here
foo(arg); // or implicitely cast here?
similarly as following would:
let arg = x: 1, y: 2; // obvious that it's a struct
foo(arg);
Anyway, I think one of the reasons why Rust currently requires field names in struct declaration is to make it obvious without consulting struct definition and being immune to reordering of fields. Let's keep it that way and let tuples stay tuples.
You canāt fix every ergonomic issue with a macro. For one, the documentation of the field names of this argument struct will be separate from the documentation function that takes those arguments. The languages that encourage passing structs when there are too many arguments are also languages without keyword arguments, so I reject that line of reasoning (at least, I canāt think of an API in Python that does this instead of using keyword arguments). I really donāt see what is gained by convoluting the otherwise interesting feature of default member values for something that it is very ill suited for. In particular, I still find that all of the proposed syntaxes are inferior to the procedural macro I made to solve this problem.
Additionaly, this duality is similar to struct variants and tuple variants with a struct value. I find them both useful at different times.
We should make struct sugar as good as possible before we decide whether to put it in Rust. I think the summary is based on results from the analysis, but there could be more analysis to improve it. What we want is as few rules as possible that cover all cases.
Instead of implicit casting from tuples, we can desugar
x, y
into
Foo { x: x, y: y }
This preserves ordering and requires the names to match. It will give errors when names do not match if the function takes a struct, but not for normal funtion arguments.
When refactoring, you always use a named struct, so we can drop struct tuples from the RFC.