From 93fad25f76bf8faf4b0000ce798339daf8e9a674 Mon Sep 17 00:00:00 2001 From: Taylor Cramer Date: Thu, 23 Feb 2017 00:35:21 -0800 Subject: [PATCH 01/22] First stab at variadic generics --- text/0000-variadic-generics.md | 172 +++++++++++++++++++++++++++++++++ 1 file changed, 172 insertions(+) create mode 100644 text/0000-variadic-generics.md diff --git a/text/0000-variadic-generics.md b/text/0000-variadic-generics.md new file mode 100644 index 00000000000..e14a03afa64 --- /dev/null +++ b/text/0000-variadic-generics.md @@ -0,0 +1,172 @@ +- Feature Name: `variadic_generics` +- Start Date: 2017-2-22 +- RFC PR: (leave this empty) +- Rust Issue: (leave this empty) + +# Summary +[summary]: #summary + +This RFC proposes the addition of several features to support variadic generics: +- An intrinsic `Tuple` trait implemented exclusively by tuples +- `(Head, ...Tail)` syntax for tuple types where `Tail: Tuple` +- `let (head, ...tail) = tuple;` pattern-matching syntax for tuples +- `let tuple = (head, tail...);` syntax for joining an element with a tuple + +# Motivation +[motivation]: #motivation + +Variadic generics are a powerful and useful tool commonly requested by Rust +users (see +[#376](https://github.com/rust-lang/rfcs/issues/376) and +[#1921](https://github.com/rust-lang/rfcs/pull/1921)). They allow +programmers to abstract over non-homogeneous collections, and they make it +possible to implement functions which accept arguments of varying length and +type. + +Rust has a demonstrable need for variadic generics. + +In Rust's own standard library, there are a number of traits which have +been repeatedly implemented for tuples of varying size up to length 12 using +macros. This approach has several downsides: +- It presents arbitrary restrictions on tuples of size 13+. +- It increases the size of the generated code, resulting in slow compile times. +- It complicates documentation +(see the list of trait implementations in +[this documentation](https://doc.rust-lang.org/std/primitive.tuple.html)). + +These arbitrary tuple-length restrictions, manual tuple macros, and confusing +documentation all combine to increase Rust's learning curve. + +Furthermore, community library authors are required to implement similar +macro-based approaches in order to implement traits for tuples. In the `Diesel` +crate, it was discovered that replacing macro-generated tuple implementations +with a structurally-recursive implementation (such as the one proposed here) +resulted in a 50% decrease in the amount of code generated and a 70% decrease +in compile times ([link](https://github.com/diesel-rs/diesel/pull/747)). This +demonstrates that Rust's lack of variadic generics is resulting in a subpar +edit-compile-debug cycle for at least one prominent, high-quality crate. + +The solution proposed here would resolve the limitations above by making it +possible to implement traits for tuples of arbitrary length. This change would +make Rust libraries easier to understand and improve the edit-compile-debug +cycle when using variadic code. + + +# Detailed design +[design]: #detailed-design + +## The `Tuple` Trait +The following would be implemented by all tuple types: +```rust +trait Tuple { + type AsRefs<'a>: Tuple + 'a; + type AsMuts<'a>: Tuple + 'a; + fn elements_as_refs<'a>(&'a self) -> Self::AsRefs<'a>; + fn elements_as_mut<'a>(&'a mut self) -> Self::AsMuts<'a>; +} +``` + +TODO: should the above use `TupleRef<'a>` and `TupleMut<'b>` traits to avoid +dependency on ACTs? It seems nicer to have them all together in one trait, but +it's probably not worth the resulting feature-stacking mess. + +The types `AsRefs` and `AsMuts` are the corresponding tuples of references to +each element in the original tuple. For example, +`(A, B, C)::AsRefs = (&A, &B, &C)` and +`(A, B, C)::AsMuts = (&mut A, &mut B, &mut C)` + +The `Tuple` trait should only be implemented for tuples. This would allow +coherence and type-checking to be extended to assume that no implementations +of `Tuple` will be added. This enables an increased level of negative +reasoning making it easier to write blanket implementations of traits for +tuples. + +## The `(Head, ...Tail)` Type Syntax +This syntax would allow for a `Cons`-cell-like representation of tuple types. +For example, `(A, ...(B, C))` would be equivalent to `(A, B, C)`. This allows +users to represent the type of tuples in an inductive style when writing trait +implementations. + +## The `(head, ...tail)` Pattern-Matching Syntax +This syntax allows for splitting apart the head and tail of a tuple. For +example, `let (head, ...tail) = (1, 2, 3);` moves the head value, `1`, into +`head`, and the tail value, `(2, 3)`, into `tail`. + +## The `(head, tail...)` Joining Syntax +This syntax allows pushing an element onto a tuple. It is the natural inverse +of the pattern-matching operation above. For example, +`let tuple = (1, (2, 3)...);` would result in `tuple` having a value of +`(1, 2, 3)`. + +## An Example + +Using the tools defined above, it is possible to implement `TupleMap`, a +trait which can apply a mapping function over all elements of a tuple: + +```rust +trait TupleMap: Tuple { + type Out: Tuple; + fn map(self, f: F) -> Self::Out; +} + +impl TupleMap for () { + type Out = (); + fn map(self, _: F) {} +} + +impl TupleMap for (Head, ...Tail) + where + F: Fn(Head) -> R, + Tail: TupleMap, +{ + type Out = (R, ...>::Out); + + fn map(self, f: F) -> Self::Out { + let (head, ...tail) = self; + let mapped_head = f(head); + let mapped_tail = tail.map(f); + (mapped_head, mapped_tail...) + } +} +``` + +This example is derived from +[a playground example by @eddyb]() +that provided inspiration for this RFC. + +The example demonstrates the concise, expressive code enabled +by this RFC. In order to implement a trait for tuples of any length, all +that was necessary was to implement the trait for `()` and `(Head, ...Tail)`. + +# Drawbacks +[drawbacks]: #drawbacks + +As with any additions to the language, this RFC would increase the number +of features present in Rust, potentially resulting increased complexity +of the language. + +There is also some unfortunate overlap between the proposed `(head, tail...)` +syntax and the current inclusive range syntax. However, the similarity +between `start...end` and `tail...` can be disambiguiated by whether or not +there is an expression immediately following the ellipsis. + +# Alternatives +[alternatives]: #alternatives + +- Do nothing. +- Implement one of the other variadic designs, such as +[#1582](https://github.com/rust-lang/rfcs/pull/1582) or +[#1921](https://github.com/rust-lang/rfcs/pull/1921) +- Implement this RFC, but leave out the extra-special coherence rules for +`Tuple`. This would simplify the implementation of the `Tuple` intrinsic +and remove some of the additional complexity it introduces. However, this +also makes it much more challenging to write coherent blanket +implementations for `Tuple`s. + +# Unresolved questions +[unresolved]: #unresolved-questions +It might be useful in the future to expand on the locations where `Type...` +can be used. Potential extensions to this RFC could allow `Type...` in +non-tuple traits. This would allow traits to use variadic generics without +explicit tuples. For instance, there has been discussion around allowing +things like `Index` to enable `foo[i, j]` syntax. From 7d3f7006a86a6153bc1886ad49bfd16e08a120cc Mon Sep 17 00:00:00 2001 From: Taylor Cramer Date: Thu, 23 Feb 2017 12:10:30 -0800 Subject: [PATCH 02/22] Mention fn Args... in unresolved questions --- text/0000-variadic-generics.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/text/0000-variadic-generics.md b/text/0000-variadic-generics.md index e14a03afa64..7d4be035b71 100644 --- a/text/0000-variadic-generics.md +++ b/text/0000-variadic-generics.md @@ -167,6 +167,7 @@ implementations for `Tuple`s. [unresolved]: #unresolved-questions It might be useful in the future to expand on the locations where `Type...` can be used. Potential extensions to this RFC could allow `Type...` in -non-tuple traits. This would allow traits to use variadic generics without -explicit tuples. For instance, there has been discussion around allowing -things like `Index` to enable `foo[i, j]` syntax. +non-tuple generics or in function argument types, lke `fn foo(args: Args...)`. +This would allow functions and traits to use variadic generics without +explicit tuples. This could enable things like the proposed `foo[i, j]` syntax +using `Index`. From 5f0aa8520886fecd679a2b970e679a27c56c764d Mon Sep 17 00:00:00 2001 From: Taylor Cramer Date: Thu, 23 Feb 2017 12:41:05 -0800 Subject: [PATCH 03/22] Add how we teach this section --- text/0000-variadic-generics.md | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/text/0000-variadic-generics.md b/text/0000-variadic-generics.md index 7d4be035b71..d604b286ba4 100644 --- a/text/0000-variadic-generics.md +++ b/text/0000-variadic-generics.md @@ -138,6 +138,37 @@ The example demonstrates the concise, expressive code enabled by this RFC. In order to implement a trait for tuples of any length, all that was necessary was to implement the trait for `()` and `(Head, ...Tail)`. +# How We Teach This +[teach]: #teach + +The `(head, ...tail)` and `(Head, ...Tail)` syntax closely mirror established +patterns for working with `Cons`-cell based lists. Rustaceans coming from +other functional programming languages will likely be familiar with the concept +of recursively-defined lists. For those unfamiliar with `Cons`-based +lists, the concept should be introduced using "structural recursion": there's +a base case, `()`, and a recursive/inductive case: `(Head, ...Tail)`. Any tuple +can be thought of in this way +(for example, `(A, B, C)` is equivalent to `(A, ...(B, ...(C, ...())))`). + +The exact mechanisms used to teach this should be determined after getting more +experience with how Rustaceans learn. After all, Rust users are a diverse crowd, +so the "best" way to teach one person might not work as well for another. There +will need to be some investigation into which explanations are more +suitable to a general audience. + +As for the `(head, tail...)` joining syntax, this should be explained as +taking each part of the tail (e.g. `(2, 3, 4)`) and inlining or un-"tupling" +them (e.g. `2, 3, 4`). This is nicely symmetrical with the `(head, ...tail)` +pattern-matching syntax. + +The `Tuple` trait is a bit of an oddity. It is probably best not to go too +far into the weeds when explaining it to new users. The extra coherence +benefits will likely go unnoticed by new users, as they allow for more +advanced features and wouldn't result in an error where one didn't exist +before. The obvious exception is when trying to implement the `Tuple` trait. +Attempts to implement `Tuple` should resort in a relevant error message, +such as "The `Tuple` trait cannot be implemented for custom types." + # Drawbacks [drawbacks]: #drawbacks From e75603917d745382db010f985aae149f5df2a16a Mon Sep 17 00:00:00 2001 From: Taylor Cramer Date: Thu, 23 Feb 2017 13:05:04 -0800 Subject: [PATCH 04/22] Fix misordered ellipses --- text/0000-variadic-generics.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/text/0000-variadic-generics.md b/text/0000-variadic-generics.md index d604b286ba4..30c66578831 100644 --- a/text/0000-variadic-generics.md +++ b/text/0000-variadic-generics.md @@ -196,9 +196,10 @@ implementations for `Tuple`s. # Unresolved questions [unresolved]: #unresolved-questions -It might be useful in the future to expand on the locations where `Type...` -can be used. Potential extensions to this RFC could allow `Type...` in -non-tuple generics or in function argument types, lke `fn foo(args: Args...)`. +It might be useful in the future to expand on the locations where `...Type` +can be used. Potential extensions to this RFC could allow `...Type` in +non-tuple generics or in function argument types, like +`fn foo(args: ...Args)`. This would allow functions and traits to use variadic generics without explicit tuples. This could enable things like the proposed `foo[i, j]` syntax -using `Index`. +using`Index`. From 2166577bfd957ccd319bcea6c363ef3eaa9ca5c5 Mon Sep 17 00:00:00 2001 From: Taylor Cramer Date: Thu, 23 Feb 2017 13:28:01 -0800 Subject: [PATCH 05/22] Make ellipsis syntax consistent --- text/0000-variadic-generics.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/text/0000-variadic-generics.md b/text/0000-variadic-generics.md index 30c66578831..9a00e687879 100644 --- a/text/0000-variadic-generics.md +++ b/text/0000-variadic-generics.md @@ -10,7 +10,7 @@ This RFC proposes the addition of several features to support variadic generics: - An intrinsic `Tuple` trait implemented exclusively by tuples - `(Head, ...Tail)` syntax for tuple types where `Tail: Tuple` - `let (head, ...tail) = tuple;` pattern-matching syntax for tuples -- `let tuple = (head, tail...);` syntax for joining an element with a tuple +- `let tuple = (head, ...tail);` syntax for joining an element with a tuple # Motivation [motivation]: #motivation @@ -92,10 +92,10 @@ This syntax allows for splitting apart the head and tail of a tuple. For example, `let (head, ...tail) = (1, 2, 3);` moves the head value, `1`, into `head`, and the tail value, `(2, 3)`, into `tail`. -## The `(head, tail...)` Joining Syntax +## The `(head, ...tail)` Joining Syntax This syntax allows pushing an element onto a tuple. It is the natural inverse of the pattern-matching operation above. For example, -`let tuple = (1, (2, 3)...);` would result in `tuple` having a value of +`let tuple = (1, ...(2, 3));` would result in `tuple` having a value of `(1, 2, 3)`. ## An Example @@ -156,7 +156,7 @@ so the "best" way to teach one person might not work as well for another. There will need to be some investigation into which explanations are more suitable to a general audience. -As for the `(head, tail...)` joining syntax, this should be explained as +As for the `(head, ...tail)` joining syntax, this should be explained as taking each part of the tail (e.g. `(2, 3, 4)`) and inlining or un-"tupling" them (e.g. `2, 3, 4`). This is nicely symmetrical with the `(head, ...tail)` pattern-matching syntax. @@ -176,10 +176,10 @@ As with any additions to the language, this RFC would increase the number of features present in Rust, potentially resulting increased complexity of the language. -There is also some unfortunate overlap between the proposed `(head, tail...)` +There is also some unfortunate overlap between the proposed `(head, ...tail)` syntax and the current inclusive range syntax. However, the similarity -between `start...end` and `tail...` can be disambiguiated by whether or not -there is an expression immediately following the ellipsis. +between `start...end` and `...tail` can be disambiguiated by whether or not +there is an expression immediately before the ellipsis. # Alternatives [alternatives]: #alternatives From 4a021aaddf8458892a29fcfa75cad1c4f4118b11 Mon Sep 17 00:00:00 2001 From: Taylor Cramer Date: Thu, 23 Feb 2017 14:28:09 -0800 Subject: [PATCH 06/22] Fix silly ATC typo --- text/0000-variadic-generics.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-variadic-generics.md b/text/0000-variadic-generics.md index 9a00e687879..2b8c5425f27 100644 --- a/text/0000-variadic-generics.md +++ b/text/0000-variadic-generics.md @@ -67,7 +67,7 @@ trait Tuple { ``` TODO: should the above use `TupleRef<'a>` and `TupleMut<'b>` traits to avoid -dependency on ACTs? It seems nicer to have them all together in one trait, but +dependency on ATCs? It seems nicer to have them all together in one trait, but it's probably not worth the resulting feature-stacking mess. The types `AsRefs` and `AsMuts` are the corresponding tuples of references to From ba6c758ded5b01e7b86d78627e0ac9a171e2fff6 Mon Sep 17 00:00:00 2001 From: Taylor Cramer Date: Thu, 23 Feb 2017 14:49:29 -0800 Subject: [PATCH 07/22] Specify is #[fundamental] --- text/0000-variadic-generics.md | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/text/0000-variadic-generics.md b/text/0000-variadic-generics.md index 2b8c5425f27..a75c6fd4f86 100644 --- a/text/0000-variadic-generics.md +++ b/text/0000-variadic-generics.md @@ -75,11 +75,13 @@ each element in the original tuple. For example, `(A, B, C)::AsRefs = (&A, &B, &C)` and `(A, B, C)::AsMuts = (&mut A, &mut B, &mut C)` -The `Tuple` trait should only be implemented for tuples. This would allow -coherence and type-checking to be extended to assume that no implementations -of `Tuple` will be added. This enables an increased level of negative -reasoning making it easier to write blanket implementations of traits for -tuples. +The `Tuple` trait should only be implemented for tuples and marked with the +`#[fundamental]` attribute described in +[the coherence RFC](https://github.com/rust-lang/rfcs/blob/master/text/1023-rebalancing-coherence.md). +This would allow coherence and type-checking to be extended to assume that no +implementations of `Tuple` will be added. This enables an increased level of +negative reasoning making it easier to write blanket implementations of traits +for tuples. ## The `(Head, ...Tail)` Type Syntax This syntax would allow for a `Cons`-cell-like representation of tuple types. @@ -188,11 +190,6 @@ there is an expression immediately before the ellipsis. - Implement one of the other variadic designs, such as [#1582](https://github.com/rust-lang/rfcs/pull/1582) or [#1921](https://github.com/rust-lang/rfcs/pull/1921) -- Implement this RFC, but leave out the extra-special coherence rules for -`Tuple`. This would simplify the implementation of the `Tuple` intrinsic -and remove some of the additional complexity it introduces. However, this -also makes it much more challenging to write coherent blanket -implementations for `Tuple`s. # Unresolved questions [unresolved]: #unresolved-questions From 7914e656c7d0e5574e0f73fcf657c34ba4b44300 Mon Sep 17 00:00:00 2001 From: Taylor Cramer Date: Sun, 26 Feb 2017 12:33:34 -0800 Subject: [PATCH 08/22] Add a note about and alternative --- text/0000-variadic-generics.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/text/0000-variadic-generics.md b/text/0000-variadic-generics.md index a75c6fd4f86..224da6d81c1 100644 --- a/text/0000-variadic-generics.md +++ b/text/0000-variadic-generics.md @@ -190,6 +190,12 @@ there is an expression immediately before the ellipsis. - Implement one of the other variadic designs, such as [#1582](https://github.com/rust-lang/rfcs/pull/1582) or [#1921](https://github.com/rust-lang/rfcs/pull/1921) +- Include explicit `Head`, `Tail`, and `Cons` associated types in the `Tuple` +trait. This could allow the above syntax to be implemented purely as sugar. +However, this approach introduces a lot of additional complexity. One of the +complications is that such a trait couldn't be implemented for `()`, so +there would have to be separate `Cons` and `Split` traits, rather than one +unified `Tuple`. # Unresolved questions [unresolved]: #unresolved-questions From f5a6bd52955d719d307bf19d401d90d5b63d72fa Mon Sep 17 00:00:00 2001 From: Taylor Cramer Date: Mon, 27 Feb 2017 16:00:52 -0800 Subject: [PATCH 09/22] Move unresolved ATC question to proper section --- text/0000-variadic-generics.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/text/0000-variadic-generics.md b/text/0000-variadic-generics.md index 224da6d81c1..05f61de0b67 100644 --- a/text/0000-variadic-generics.md +++ b/text/0000-variadic-generics.md @@ -66,10 +66,6 @@ trait Tuple { } ``` -TODO: should the above use `TupleRef<'a>` and `TupleMut<'b>` traits to avoid -dependency on ATCs? It seems nicer to have them all together in one trait, but -it's probably not worth the resulting feature-stacking mess. - The types `AsRefs` and `AsMuts` are the corresponding tuples of references to each element in the original tuple. For example, `(A, B, C)::AsRefs = (&A, &B, &C)` and @@ -199,10 +195,15 @@ unified `Tuple`. # Unresolved questions [unresolved]: #unresolved-questions -It might be useful in the future to expand on the locations where `...Type` +-It might be useful in the future to expand on the locations where `...Type` can be used. Potential extensions to this RFC could allow `...Type` in non-tuple generics or in function argument types, like `fn foo(args: ...Args)`. This would allow functions and traits to use variadic generics without explicit tuples. This could enable things like the proposed `foo[i, j]` syntax using`Index`. +-Should the `Tuple` trait use separate `TupleRef<'a>` and `TupleMut<'b>` traits +to avoid dependency on ATCs? It seems nicer to have them all together in one +trait, but it might not be worth the resulting feature-stacking mess. + + From 0a18520407186546497e6af2b0d30cce6f0f4db6 Mon Sep 17 00:00:00 2001 From: Taylor Cramer Date: Mon, 27 Feb 2017 16:10:55 -0800 Subject: [PATCH 10/22] Fix another ellipsis typo --- text/0000-variadic-generics.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-variadic-generics.md b/text/0000-variadic-generics.md index 05f61de0b67..377ac03ea7c 100644 --- a/text/0000-variadic-generics.md +++ b/text/0000-variadic-generics.md @@ -123,7 +123,7 @@ impl TupleMap for (Head, ...Tail) let (head, ...tail) = self; let mapped_head = f(head); let mapped_tail = tail.map(f); - (mapped_head, mapped_tail...) + (mapped_head, ...mapped_tail) } } ``` From 97f402d1f19ae021e20456c3fce236ef5690bede Mon Sep 17 00:00:00 2001 From: Taylor Cramer Date: Mon, 27 Feb 2017 22:20:50 -0800 Subject: [PATCH 11/22] Add missing link --- text/0000-variadic-generics.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-variadic-generics.md b/text/0000-variadic-generics.md index 377ac03ea7c..21d5a80adc2 100644 --- a/text/0000-variadic-generics.md +++ b/text/0000-variadic-generics.md @@ -129,7 +129,7 @@ impl TupleMap for (Head, ...Tail) ``` This example is derived from -[a playground example by @eddyb]() +[a playground example by @eddyb](https://play.rust-lang.org/?gist=8fd29c83271f3e8744a3f618786ca1de&version=nightly&backtrace=0) that provided inspiration for this RFC. The example demonstrates the concise, expressive code enabled From 8114d4c1d42b3f4dd05418a62542c1ecd43a4120 Mon Sep 17 00:00:00 2001 From: Taylor Cramer Date: Mon, 27 Feb 2017 22:21:40 -0800 Subject: [PATCH 12/22] Remove temp variables in example --- text/0000-variadic-generics.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/text/0000-variadic-generics.md b/text/0000-variadic-generics.md index 21d5a80adc2..dc0ce47b6c5 100644 --- a/text/0000-variadic-generics.md +++ b/text/0000-variadic-generics.md @@ -121,9 +121,7 @@ impl TupleMap for (Head, ...Tail) fn map(self, f: F) -> Self::Out { let (head, ...tail) = self; - let mapped_head = f(head); - let mapped_tail = tail.map(f); - (mapped_head, ...mapped_tail) + (f(head), ...tail.map(f)) } } ``` From 5f52e3844cf6958f738a4c33dfba24f21e7293d8 Mon Sep 17 00:00:00 2001 From: Taylor Cramer Date: Mon, 27 Feb 2017 22:45:33 -0800 Subject: [PATCH 13/22] Explicitly allow (...Front, Last) and similar --- text/0000-variadic-generics.md | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/text/0000-variadic-generics.md b/text/0000-variadic-generics.md index dc0ce47b6c5..b42a2554f01 100644 --- a/text/0000-variadic-generics.md +++ b/text/0000-variadic-generics.md @@ -79,18 +79,18 @@ implementations of `Tuple` will be added. This enables an increased level of negative reasoning making it easier to write blanket implementations of traits for tuples. -## The `(Head, ...Tail)` Type Syntax -This syntax would allow for a `Cons`-cell-like representation of tuple types. -For example, `(A, ...(B, C))` would be equivalent to `(A, B, C)`. This allows -users to represent the type of tuples in an inductive style when writing trait -implementations. +## The `...T` Type Syntax +This syntax would allow for expansion of tuple types into a list of types. +For example, `(A, B, C)` could be represented as `(A, ...(B, C))` or +`(...(A, B), C)`. This allows users to express type lists of varying arity. -## The `(head, ...tail)` Pattern-Matching Syntax +## The `...x` Pattern-Matching Syntax This syntax allows for splitting apart the head and tail of a tuple. For example, `let (head, ...tail) = (1, 2, 3);` moves the head value, `1`, into -`head`, and the tail value, `(2, 3)`, into `tail`. +`head`, and the tail value, `(2, 3)`, into `tail`. Similarly, the last element +of a tuple could be matched out using `let (...front, last) = (1, 2, 3);`. -## The `(head, ...tail)` Joining Syntax +## The `...x` Joining Syntax This syntax allows pushing an element onto a tuple. It is the natural inverse of the pattern-matching operation above. For example, `let tuple = (1, ...(2, 3));` would result in `tuple` having a value of @@ -146,6 +146,11 @@ a base case, `()`, and a recursive/inductive case: `(Head, ...Tail)`. Any tuple can be thought of in this way (for example, `(A, B, C)` is equivalent to `(A, ...(B, ...(C, ...())))`). +When teaching this new syntax, it is important to note that the proposed system +allows for more complicated matching than traditional `Cons`-lists. For example, +it is possible to match on `(...Front, Last)` in addition to the familiar +`(Head, ...Tail)`-style matching. + The exact mechanisms used to teach this should be determined after getting more experience with how Rustaceans learn. After all, Rust users are a diverse crowd, so the "best" way to teach one person might not work as well for another. There @@ -193,6 +198,7 @@ unified `Tuple`. # Unresolved questions [unresolved]: #unresolved-questions + -It might be useful in the future to expand on the locations where `...Type` can be used. Potential extensions to this RFC could allow `...Type` in non-tuple generics or in function argument types, like From 3d6984ee67548b94c8c494cfb241d2dbb7c36212 Mon Sep 17 00:00:00 2001 From: Taylor Cramer Date: Mon, 27 Feb 2017 22:52:34 -0800 Subject: [PATCH 14/22] Update note about range syntax in response to feedback --- text/0000-variadic-generics.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/text/0000-variadic-generics.md b/text/0000-variadic-generics.md index b42a2554f01..02a947a82fb 100644 --- a/text/0000-variadic-generics.md +++ b/text/0000-variadic-generics.md @@ -178,9 +178,10 @@ of features present in Rust, potentially resulting increased complexity of the language. There is also some unfortunate overlap between the proposed `(head, ...tail)` -syntax and the current inclusive range syntax. However, the similarity -between `start...end` and `...tail` can be disambiguiated by whether or not -there is an expression immediately before the ellipsis. +syntax and the current inclusive range syntax. There will need to be some +discussion as to how best to overcome this. One possible path could be to +use `tail...` instead, as it's not obvious that `x...` is distinct from +`x..` in a useful way (inclusive vs. exclusive range to infinity). # Alternatives [alternatives]: #alternatives From 75064989dd0d03527af8727e9d6cc47465abd199 Mon Sep 17 00:00:00 2001 From: Taylor Cramer Date: Mon, 27 Feb 2017 23:00:22 -0800 Subject: [PATCH 15/22] More cleanup from allowing (...Front, Last) --- text/0000-variadic-generics.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/text/0000-variadic-generics.md b/text/0000-variadic-generics.md index 02a947a82fb..4a26dfd074d 100644 --- a/text/0000-variadic-generics.md +++ b/text/0000-variadic-generics.md @@ -8,7 +8,7 @@ This RFC proposes the addition of several features to support variadic generics: - An intrinsic `Tuple` trait implemented exclusively by tuples -- `(Head, ...Tail)` syntax for tuple types where `Tail: Tuple` +- `...T` syntax for tuple types where `T: Tuple` - `let (head, ...tail) = tuple;` pattern-matching syntax for tuples - `let tuple = (head, ...tail);` syntax for joining an element with a tuple @@ -142,9 +142,10 @@ patterns for working with `Cons`-cell based lists. Rustaceans coming from other functional programming languages will likely be familiar with the concept of recursively-defined lists. For those unfamiliar with `Cons`-based lists, the concept should be introduced using "structural recursion": there's -a base case, `()`, and a recursive/inductive case: `(Head, ...Tail)`. Any tuple -can be thought of in this way -(for example, `(A, B, C)` is equivalent to `(A, ...(B, ...(C, ...())))`). +a base case, `()`, and a recursive/inductive case: `(Head, ...Tail)` or +`(Front..., Last)`. Any tuple can be thought of in this way +(for example, `(A, B, C)` is equivalent to `(A, ...(B, ...(C, ...())))` +and `(...(...(...(), A), B), C)`). When teaching this new syntax, it is important to note that the proposed system allows for more complicated matching than traditional `Cons`-lists. For example, From 70daa3eff9651966f1390e852d286310f5e4c2c4 Mon Sep 17 00:00:00 2001 From: Taylor Cramer Date: Mon, 27 Feb 2017 23:01:23 -0800 Subject: [PATCH 16/22] Fix unresolved questions bulletpoints --- text/0000-variadic-generics.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/text/0000-variadic-generics.md b/text/0000-variadic-generics.md index 4a26dfd074d..afb99852af5 100644 --- a/text/0000-variadic-generics.md +++ b/text/0000-variadic-generics.md @@ -201,14 +201,14 @@ unified `Tuple`. # Unresolved questions [unresolved]: #unresolved-questions --It might be useful in the future to expand on the locations where `...Type` +- It might be useful in the future to expand on the locations where `...Type` can be used. Potential extensions to this RFC could allow `...Type` in non-tuple generics or in function argument types, like `fn foo(args: ...Args)`. This would allow functions and traits to use variadic generics without explicit tuples. This could enable things like the proposed `foo[i, j]` syntax using`Index`. --Should the `Tuple` trait use separate `TupleRef<'a>` and `TupleMut<'b>` traits +- Should the `Tuple` trait use separate `TupleRef<'a>` and `TupleMut<'b>` traits to avoid dependency on ATCs? It seems nicer to have them all together in one trait, but it might not be worth the resulting feature-stacking mess. From f228bebcf6a94200dd6286fc9b0d9a5d96514482 Mon Sep 17 00:00:00 2001 From: Taylor Cramer Date: Mon, 27 Feb 2017 23:15:25 -0800 Subject: [PATCH 17/22] Remove mention of Cons list from how we teach section --- text/0000-variadic-generics.md | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/text/0000-variadic-generics.md b/text/0000-variadic-generics.md index afb99852af5..2d8c8989dbf 100644 --- a/text/0000-variadic-generics.md +++ b/text/0000-variadic-generics.md @@ -137,15 +137,13 @@ that was necessary was to implement the trait for `()` and `(Head, ...Tail)`. # How We Teach This [teach]: #teach -The `(head, ...tail)` and `(Head, ...Tail)` syntax closely mirror established -patterns for working with `Cons`-cell based lists. Rustaceans coming from -other functional programming languages will likely be familiar with the concept -of recursively-defined lists. For those unfamiliar with `Cons`-based -lists, the concept should be introduced using "structural recursion": there's -a base case, `()`, and a recursive/inductive case: `(Head, ...Tail)` or -`(Front..., Last)`. Any tuple can be thought of in this way -(for example, `(A, B, C)` is equivalent to `(A, ...(B, ...(C, ...())))` -and `(...(...(...(), A), B), C)`). +The `...X` syntax can be thought of as a simple syntax expansion. A good mental +tool is to think of `...` as simply dropping the parenthesis around a tuple, e.g. +`...(A, B, C)` expanding to `A, B, C`. + +This allows for operations that were previously impossible, such as appending +to the front or tail of a tuple using `(new_head, ...tuple)` or +`(...tuple, new_last)`, or even `(new_head, ...tuple, new_tail)`. When teaching this new syntax, it is important to note that the proposed system allows for more complicated matching than traditional `Cons`-lists. For example, From 91e7381fe2a0fed20a44274e1700b7114e507545 Mon Sep 17 00:00:00 2001 From: Taylor Cramer Date: Tue, 28 Feb 2017 12:20:06 -0800 Subject: [PATCH 18/22] Explicitly mention prior partial tail borrows proposals --- text/0000-variadic-generics.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/text/0000-variadic-generics.md b/text/0000-variadic-generics.md index 2d8c8989dbf..a5a40d7bca1 100644 --- a/text/0000-variadic-generics.md +++ b/text/0000-variadic-generics.md @@ -195,6 +195,15 @@ However, this approach introduces a lot of additional complexity. One of the complications is that such a trait couldn't be implemented for `()`, so there would have to be separate `Cons` and `Split` traits, rather than one unified `Tuple`. +- Allow partial borrows or tuple reference splitting. Previous proposals have +included a transformation from `&(Head, Tail...)` to `(&Head, &Tail...)`. +However, this would require that every tuple `(Head, Tail...)` actually +contain a sub-tuple `tail...`. This would get in the way of desirable +field-reordering optimizations. +In order to avoid this restriction, this proposal includes a transformation +from `&(A, B, C)` to a tuple containing references to all the individual types: +`(&A, &B, &C)`. Iteration over tuples is then performed by-value (where the +values are references). # Unresolved questions [unresolved]: #unresolved-questions From 5eadb6f68d996f6bd9490245aba5306dc08e9e97 Mon Sep 17 00:00:00 2001 From: Taylor Cramer Date: Tue, 28 Feb 2017 15:37:40 -0800 Subject: [PATCH 19/22] Clarify tuple sub-refs section --- text/0000-variadic-generics.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/text/0000-variadic-generics.md b/text/0000-variadic-generics.md index a5a40d7bca1..b02bfca593e 100644 --- a/text/0000-variadic-generics.md +++ b/text/0000-variadic-generics.md @@ -196,11 +196,11 @@ complications is that such a trait couldn't be implemented for `()`, so there would have to be separate `Cons` and `Split` traits, rather than one unified `Tuple`. - Allow partial borrows or tuple reference splitting. Previous proposals have -included a transformation from `&(Head, Tail...)` to `(&Head, &Tail...)`. +included a transformation from `&(Head, Tail...)` to `(&Head, &Tail)`. However, this would require that every tuple `(Head, Tail...)` actually -contain a sub-tuple `tail...`. This would get in the way of desirable -field-reordering optimizations. -In order to avoid this restriction, this proposal includes a transformation +contain a sub-tuple `Tail` (i.e. `(A, B, C)` would need to contain `(B, C)`). +This would get in the way of desirable field-reordering optimizations. +In order to avoid these restriction, this proposal includes a transformation from `&(A, B, C)` to a tuple containing references to all the individual types: `(&A, &B, &C)`. Iteration over tuples is then performed by-value (where the values are references). From 5163037a283fc254efe5e4924f0c5181ea136d23 Mon Sep 17 00:00:00 2001 From: Taylor Cramer Date: Tue, 28 Feb 2017 20:47:46 -0800 Subject: [PATCH 20/22] Fix silly typo --- text/0000-variadic-generics.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-variadic-generics.md b/text/0000-variadic-generics.md index b02bfca593e..d0201ecf0e0 100644 --- a/text/0000-variadic-generics.md +++ b/text/0000-variadic-generics.md @@ -200,7 +200,7 @@ included a transformation from `&(Head, Tail...)` to `(&Head, &Tail)`. However, this would require that every tuple `(Head, Tail...)` actually contain a sub-tuple `Tail` (i.e. `(A, B, C)` would need to contain `(B, C)`). This would get in the way of desirable field-reordering optimizations. -In order to avoid these restriction, this proposal includes a transformation +In order to avoid this restriction, this proposal includes a transformation from `&(A, B, C)` to a tuple containing references to all the individual types: `(&A, &B, &C)`. Iteration over tuples is then performed by-value (where the values are references). From 3c6e4484dc8b77dc57d3fe576aba308d9a497903 Mon Sep 17 00:00:00 2001 From: Taylor Cramer Date: Fri, 3 Mar 2017 11:29:30 -0800 Subject: [PATCH 21/22] Add Working With References section --- text/0000-variadic-generics.md | 52 ++++++++++++++++++++++++++++++++-- 1 file changed, 50 insertions(+), 2 deletions(-) diff --git a/text/0000-variadic-generics.md b/text/0000-variadic-generics.md index d0201ecf0e0..b8f5683649f 100644 --- a/text/0000-variadic-generics.md +++ b/text/0000-variadic-generics.md @@ -134,6 +134,53 @@ The example demonstrates the concise, expressive code enabled by this RFC. In order to implement a trait for tuples of any length, all that was necessary was to implement the trait for `()` and `(Head, ...Tail)`. +## Working With References + +Since `let (head, ...tail) = tuple;` consumes `tuple`, there is an extra step +required when implementing traits that take `&self` rather than `self`. Previous +RFC proposals have included a conversion from `&(Head, ...Tail)` to +`(&Head, &Tail)`. +Unfortunately, this would require that every tuple `(Head, Tail...)` contain a +sub-tuple `Tail` (i.e. `(A, B, C)` would need to contain `(B, C)`). +This would limit tuple iteration to a single direction and prevent desirable +field-reordering optimizations. + +Instead, this RFC proposes the use of `elements_as_refs` and `elements_as_mut` +methods to convert from `&(A, B, C)` to `(&A, &B, &C)` and from `&mut (A, B, C)` +to `(&mut A, &mut B, &mut C)`, respectively. + +Using this strategy, `Clone` can be implemented as follows: + +```rust +trait CloneRefsTuple: Tuple { + type Output: Tuple; + /// Turns `(&A, &B, ...)` into `(A, B, ...)` by cloning each element + fn clone_refs_tuple(self) -> Self::Output; +} +impl CloneRefsTuple for () { + type Output = (); + fn clone_refs_tuple(self) -> Self::Output { } +} +impl<'a, Head, Tail> CloneRefsTuple for (&'a Head, ...Tail) + where Head: Clone, Tail: CloneRefsTuple +{ + type Output = (Head, ::Output); + + fn clone_refs_tuple(self) -> Self::Output { + let (head, ...tail) = self; + (head.clone(), tail.clone_refs_tuple()) + } +} + +impl Clone for T where + for<'a> T::AsRefs<'a>: CloneRefsTuple +{ + fn clone(&self) -> Self { + self.elements_as_refs().clone_refs_tuple() + } +} +``` + # How We Teach This [teach]: #teach @@ -197,9 +244,10 @@ there would have to be separate `Cons` and `Split` traits, rather than one unified `Tuple`. - Allow partial borrows or tuple reference splitting. Previous proposals have included a transformation from `&(Head, Tail...)` to `(&Head, &Tail)`. -However, this would require that every tuple `(Head, Tail...)` actually +However, this would require that every tuple `(Head, Tail...)` contain a sub-tuple `Tail` (i.e. `(A, B, C)` would need to contain `(B, C)`). -This would get in the way of desirable field-reordering optimizations. +This would limit tuple iteration to a single direction and prevent desirable +field-reordering optimizations. In order to avoid this restriction, this proposal includes a transformation from `&(A, B, C)` to a tuple containing references to all the individual types: `(&A, &B, &C)`. Iteration over tuples is then performed by-value (where the From 609f2fc1ddea86c6804e642be6b2bd73d432e398 Mon Sep 17 00:00:00 2001 From: Taylor Cramer Date: Tue, 7 Mar 2017 15:35:27 -0800 Subject: [PATCH 22/22] Add Future Extensions section --- text/0000-variadic-generics.md | 66 ++++++++++++++++++++++++++++------ 1 file changed, 56 insertions(+), 10 deletions(-) diff --git a/text/0000-variadic-generics.md b/text/0000-variadic-generics.md index b8f5683649f..7e527c27f2c 100644 --- a/text/0000-variadic-generics.md +++ b/text/0000-variadic-generics.md @@ -229,6 +229,61 @@ discussion as to how best to overcome this. One possible path could be to use `tail...` instead, as it's not obvious that `x...` is distinct from `x..` in a useful way (inclusive vs. exclusive range to infinity). +# Future Extensions +[future]: #future-extensions + +## Allow `...T` in Argument Position +The RFC as proposed would allow for varied-length argument lists to functions +in the form of explicit tuples. It would allow variadic functions to be +declared and used like this: + +```rust +trait MyTrait: Tuple {...} + +impl MyTrait for () { ... } + +impl MyTrait for (Head, ...Tail) where Tail: Tuple, ... { ... } + +fn foo(args: T) { ... } + +// Called like this: +foo((arg1, arg2, arg3)); +``` + +However, this approach requires that functions be called with explicit tuple +arguments, rather than the more natural syntax `foo(arg1, arg2, arg3)`. +For this reason, it may be beneficial to allow `foo` to be declared like +`fn foo(...args: ...T)` and called like +`foo(arg1, arg2, arg3)`. + +## Allow `...T` in Generic Type Lists +Another possible extension would be to allow `...T` in generic type lists, +e.g. `foo<...T>`. Without this extension, a non-type-inferred call to `foo` +would be written `foo::<(T1, T2, T3)>(arg1, arg2, arg3)`. However, this is +unnecessarily verbose and doesn't allow for existing traits and functions +to support variadic argument lists. + +By supporting `...T` in generic type lists, `foo`'s declaration would become +`fn foo<...T>(...args: ...T) where T: MyTrait`, and it could be called like +`foo::(arg1, arg2, arg3)`. + +As mentioned before, this extension would allow functions, traits, and types +to backwards-compatibly support variadic argument lists. For example, the +`Index` trait could be extended to support indexing like +`my_vec[i, j]`: + +```rust +pub trait Index<...Idxs> where Idxs: Tuple + ?Sized { + type Output: ?Sized; + fn index(&self, ...indices: ...Idxs) -> &Self::Output; +} +``` + +Similarly, this extension would allow the currently awkward `fn_traits` to use +the syntax +`T: Fn` instead of the current (unstable) syntax +`T: Fn<(Arg1, Arg2, Arg3)>`. + # Alternatives [alternatives]: #alternatives @@ -255,16 +310,7 @@ values are references). # Unresolved questions [unresolved]: #unresolved-questions - -- It might be useful in the future to expand on the locations where `...Type` -can be used. Potential extensions to this RFC could allow `...Type` in -non-tuple generics or in function argument types, like -`fn foo(args: ...Args)`. -This would allow functions and traits to use variadic generics without -explicit tuples. This could enable things like the proposed `foo[i, j]` syntax -using`Index`. - Should the `Tuple` trait use separate `TupleRef<'a>` and `TupleMut<'b>` traits to avoid dependency on ATCs? It seems nicer to have them all together in one trait, but it might not be worth the resulting feature-stacking mess. - - +- Should either of the future extensions be included in the initial RFC?