-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Tuple-Based Variadic Generics #1935
Conversation
How would you actually write variadic functions under this proposal? Am I correct that you'd still have to write something like I guess the unresolved questions covers this? |
It is not a major deal breaker or anything, but it's worth noting that my field reordering stuff does currently affect tuples, and we probably want it to continue doing so where possible, I'd think. Now if only the world would leave me alone for the like 2 days I need to finish my field reordering stuff, but that's another discussion. |
@withoutboats You're correct, the current proposal would require something like |
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>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would prefer to avoid a hard dependency on ATC here, and I'm not sure the functionality needs to be in Tuple
itself.
IOW, we can do this more transparently to the user with a recursive impl
perhaps? Well, no, "compiler knows best", it could project AsRefs
partially (e.g. (A, ...B, C)::AsRefs == (&A, ...B::AsRefs, &C)
), but we can probably experiment with a recursive impl
in a library, for a trait TupleAsRefs<'a>
, to start with.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To be clear, it's likely we'll want to only stabilize the ATC form itself, for convenience's sake, but it's not needed to experiment IMO.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah-- I left a comment about this in the "unresolved questions" section. Separate traits (TupleAsRef<'a>
and TupleAsMut<'b>
) work and eliminate the dependency on ATCs, but I think it's slightly less clear to the end user.
text/0000-variadic-generics.md
Outdated
|
||
## 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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is potentially misleading, as (A, ...(B, C), D)
would also be valid. Any mention of "cons" is likely to confuse more than help, compared to, say, "tuple concatenation" (which is valid for both types and values). See #1921 (comment) and #1921 (comment).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had actually purposefully avoided discussing things like (...(A, B,) C)
(tuples in non-tail position). I wasn't sure if that was something we wanted to support in the initial implementation, but I think it makes sense to include them prior to stabilizing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@sgrif thought too (#1921 (comment)) but I disagree, from the implementation side doing it right the first time is not much harder, and it's less work overall than having two incompatible implementations at different points in time.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good to know-- you have a much better sense of the actual implementation then I do. I'll edit the RFC appropriately.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To be more clear: both tail-only and generalized single-repeat require the same primitive: a single check somewhere in the compiler that the T
in ...T
is now known to be a tuple (i.e. after substituting in type parameters, resolving associated types, etc.) therefore expand it.
And concatenating 3 lists of types is hardly any more difficult than concatenating 2 lists.
This is done only once, mind you (probably should be done when creating any tuple IMO).
On top of that, everywhere a tuple is introspected now has to take into account a partially unknown tuple, and that has the same cost for both approaches, except if you do it one way then later you have to redo in the other way, and there can even be subtle bugs introduced.
text/0000-variadic-generics.md
Outdated
let (head, ...tail) = self; | ||
let mapped_head = f(head); | ||
let mapped_tail = tail.map(f); | ||
(mapped_head, ...mapped_tail) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might be a bit clearer with the last 3 lines flattened into (f(head), ...tail.map(f))
.
text/0000-variadic-generics.md
Outdated
``` | ||
|
||
This example is derived from | ||
[a playground example by @eddyb]() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing link 😆.
text/0000-variadic-generics.md
Outdated
-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. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overlap with range syntax is a problem. I believe x...
could always work if we decide it doesn't make sense as a range, over x..
(i.e. is "inclusive at infinity" useful?).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
x...
is how you expand a parameter pack in C++, so it'd be familiar to some, for better or worse.
text/0000-variadic-generics.md
Outdated
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, ...())))`). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See #1935 (comment).
dbe25b8
to
8114d4c
Compare
@camlorn Right, and nothing in the RFC assumes field order matters, i.e. there are no "partial tail borrows" or anything like that, it can all be implemented in a library with safe code for a fixed number of arities. |
fn foo<H, T: Trait>((head, ...tail): ...(H, ...T)) {...}
fn foo<H, T: Trait>(head: H, tail: ...T) {...}
fn foo<H, T: Trait>(args: ...(H, ...T)) { let (head, ...tail) = args; ...} |
text/0000-variadic-generics.md
Outdated
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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry but ...x
already is a valid inclusive range expression (it creates a std::ops::RangeToInclusive<Tail>). This code works today in nightly:
#![feature(inclusive_range_syntax)]
fn main() {
println!("{:?}", (1, ...(2,)));
// prints `(1, ...(2,))`
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, you're correct. @eddyb made the same observation above.
de39bfd
to
7be73b6
Compare
@aturon A few of the points I listed above would benefit from some lang team discussion:
I'll also check in @withoutboats again to see if we can come to some consensus WRT their preference for first-class "type lists." |
@cramertj I don't recall where the |
@eddyb @sgrif expressed interest in it-- I'm imagining they have some use case in mind. I'd expect the desire is related to implementing a function that takes either a single argument (of type |
It makes more sense IMO to go straight to variadic functions & methods than have multiple arguments manually packaged in tuples. |
@eddyb Yes, I've been gradually drifting towards preferring an "all at once" approach myself. My initial goal was to keep this RFC as minimal as possible in the hopes that it would be merged sooner. However, in order to know what the right "first steps" are, you have to consider how they move the language towards a comprehensive solution. |
We're clearly not going to get to this before the impl period, so I propose we formally postpone it and start a fresh thread when we're ready to consider it. @rfcbot fcp postpone |
Team member @aturon has proposed to postpone this. The next step is review by the rest of the tagged teams: No concerns currently listed. Once these reviewers reach consensus, this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up! See this document for info about what commands tagged team members can give me. |
Alas, what symmetry that my first ever ticked checkbox as a lang team member should be to postpone my first ever RFC. 😄 I'm excited to work more on this next year after we've had a chance to implement the host of wonderful RFCs currently in the queue. |
🔔 This is now entering its final comment period, as per the review above. 🔔 |
1 similar comment
🔔 This is now entering its final comment period, as per the review above. 🔔 |
I'm now closing this RFC as postponed. Thanks, @cramertj and everyone else for pushing further on this issue -- something we'll definitely want to come back to in the future! |
Unfortunately, we have 28 columns, which requires Diesel's "huge-tables" support. This dramatically increases compile time of the Diesel dependency to ~3-4 minutes, up from negligible. Diesel's team says it's tracked by: - diesel-rs/diesel#747 - rust-lang/rfcs#1921 - rust-lang/rfcs#1935
Has there been any interest in reviving this lately? |
@alexreg There's still general interest in the feature, but it's not the kind of change that's targeted for the roadmap this year, so there's probably nothing going on with it right now. (Doing it and const generics could be an interesting thing to consider for next year's roadmap, once we're past the stabilize year.) |
@scottmcm Fair enough. w.r.t. Const generics, I thought they were targeted for this year? |
@alexreg const generics has an RFC merged but implementing it is no official priority of the compiler team. All of the statements were of the form "maybe we can have const generics in nightly by end of 2018"... |
Rendered
cc @eddyb @sgrif