[Pre-pre-RFC] "splatting" for named arguments and function overloading
I think there are two kinds of functions that can benefit from named, optional arguments:
- Some behave almost identical and would ideally be implemented by just having an additional argument. Most likely these will not allow mutually exclusive arguments. This is what this proposal would add. [1]
- Others need a significantly different implementation (e.g. sort_by_cached_key vs sort_by/sort_by_key). Implementing them with an additional argument would (as epage mentioned) depend on inlining and a require bit of extra boilerplate to forward to the relevant code block. Preferrably they would behave like a separate function, but without needing to be named separately. [2]
I believe we need to consider both, as both are equally valid and useful. For the first kind, a struct with options (+ syntax suggar) as proposed here is could be a good solution. For the second kind it probably makes the most sense to stay with familiar syntax and allow defining multiple implementations with the same name, but enforce that arguments with the same name MUST have the same type (or be generic over the same trait bounds). [3]
Then allow them to be mixed, thus additionally allowing restrictions on which can be combined.
struct Opts {
stable: bool,
}
struct ByOpts<F> {
by: F,
stable: bool,
}
impl Foo {
fn sort(&self, args: ...Opts) {}
fn sort<F>(&self, args: ...ByOpts<F>) {}
#[with_arg(cached=true)]
fn sort<F>(&self, args: ...ByOpts<F>) {}
}
This would let the implementation side decide which kind it needs/prefers and allow (some) flexibility in allowed combinations [4].
Yes, the above example is still simplified and has issues (but they also exist in the other proposals I saw): Mainly that the first 2 can't really be combined because the type of F could not be inferred, thus requiring two different functions even with the "splatting". Because of this I also did not include by_key or combining by and by_key. [5] It might be simpler to only use the second kind for sort, that way the generics issue can be avoided.
Personally I'd prefer a simpler syntax where the argument names are visible in the signature directly, but for sake of argument I'm going to use the syntax proposed here.
If the decision which implementation to use only depends on name + arguments they could even differ in their return type. And in the long run the return type could be considered if there is no overlap in allowed types (see specialization).
Today implemented with an options struct, builder pattern or an always-present argument ↩︎
Today implemented by using different function names. ↩︎
Forcing the same type should eliminate most (if not all) problems with C++-like overloading and type inference, as the implementation to use only depends on which (named) arguments are present, outputting a compile time error if there is a type mismatch between implementations. ↩︎
"Some" because representing "any combination but not all 4" is still problematic. ↩︎
As far as I know Rust doesn't have a default type to use if the type could not be inferred because it isn't used, so "splatting" alone would likely not help much for the sort example. ↩︎
Discussion in the ATmosphere