-
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
RFC: Tuple indexing #184
RFC: Tuple indexing #184
Conversation
|
||
```rust | ||
let x = (box 1i, box 2i); | ||
let x1 = { let (ref a, _) = x; 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.
It's not if the indexing is zero based.
Wouldn't it be better to write a struct at the point you want to store and extract values from a tuple as a conglomerate? As I understand it, tuples are only really useful as a light-weight way of moving multiple values out of (and sometimes into) functions. Allowing |
👎: if you’re wanting to do things like this, you probably shouldn’t be using tuples. Personally I feel that it would be justifiable to give the guidance that tuples should only be used for simple generic arguments (where a function may accept a I would actually rather like |
👍 I think this is useful. I think it should also work on tuple-structs (e.g. |
@dhardy You cannot write a macro like that. Macros don't know about types, so you cannot construct a proper destructuring pattern for the tuple. Even if you could, such a macro could then not produce an lvalue to allow for assignment, which I believe this syntax should allow, e.g. let mut x = (1u, 2i, '3');
x.0 = 42u; For reference, Swift allows this tuple indexing syntax, and allows for assignment with it. |
@kballard I don't think Rust should include features just because Swift does. I agree with CM on this. |
+1 swift has a lot of nice pragmatic tweaks, and this is one of them, IMO. this would be particularly useful with tuple-structs. There area times when the name of the type , and the types or positions of the components are sufficient information, and field names are just clutter. (vica versa, I would also like to see anonymous structs, but more ergonomic tuple structs would cover some of those cases). tuple structs also have the draw that the constructor is nice, but the repulsion that they're currently awkward to use (you must repeat the typename for destructuring). you can roll accessors with a macro but they also clutter your code and make it look more expensive than it is (and those macro definitions are another dependancy for libraries) Tangential but related, Committing to names before you've explored a problem is tiresome |
+1. This would be very convenient, especially, as others have already said, with tuple structs. |
Incidentally, I don't think it makes sense to define this RFC in terms of the equivalent destructuring code. That just raises questions on edge cases that haven't been considered (or even not-so-edge cases, like assignment to a tuple index). I think it makes much more sense just to define tuple indexing as equivalent to using named fields, so the first field in a tuple implicitly has the field name |
Big 👍 Indexing on tuple structs makes the newtype idiom much easier to write. I'd be able to replace all those ugly |
FWIW, you can get this now by just using a normal struct with a field name: |
Yep. I think this is the right idea, and the proposal is just closing a features gap with respect to |
tuple structs. This syntax is recognised wherever an integer or float literal is | ||
found in place of the normal field or method name expected when accessing fields | ||
with `.`. Float literals in this position are expanded into two field accesses, | ||
so that an expression of the form `a.1.3` is equivalent to `(a.1).3`. |
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 agree that a.1.3
should be legal and equivalent to (a.1).3
, but isn't there a way to do this without introducing the awkwardness of float literals in the first place? I.e. just say that valid tokens following the .
operator are either an identifier (named struct field, method) or a decimal number? And a.1.3
is just the same thing occuring twice.
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.
@glaebhoerl I would imagine (I am not well aquainted with the internals of the rust compiler) that float literals become involved because this RFC would probably be implemented by modifying the parser, whereas float literals are a type of token output by the lexer (since the lexer typically doesn't have much of a notion of context).
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.
+1
|
||
Allow indexing of tuples and tuple structs: this has the advantage of | ||
consistency, but the disadvantage of not being checked for out-of-bounds errors | ||
at compile 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.
As also pointed out on reddit, indexing as such (i.e. tuple[0]
, tuple[1]
etc.) wouldn't make sense as an alternative, because tuples are heterogenous. (They definitely couldn't be made to implement the Index
* traits.)
👍 this seems to be a no brainer ergonomics improvement. |
@bachm: yes, it improves the ergonomics of using tuples—but do we want to be doing that? I do not believe that we should be: such techniques are not good for code readability; I believe that they should always (yes, always—I will not even use |
@chris-morgan yes, that also make sense from a certain point of view. However, currently |
@bachm: I would say it the other way round: we have |
Tuples and structs are not as alike as you think. Tuples probably can't have layout optimization. They are heterogeneous sequences, not records for accessing fields. |
I have to say @chris-morgan has a good point. In my experience, people tend to overuse tuples to the point where it hurst readability. A struct with named fields is almost always a better idea, and in Rust we have destructuring which makes pulling out relevant parts of tuples from a function returning a tuple really easy. So while this change is an ergonomic improvement, by implementing it we might be steering people to sub-par designs. |
The argument that a feature can be abused to write code with bad readability, and that therefore, it's better to not have the feature, is unpersuasive to me in general. It's true of most features. I prefer to leave issues of style and taste up to the programmer, because "one size fits all" rules generally don't. The proper avenues for encouraging good style, in my opinion, are through example-setting, cultural norms, education & community pressure, style guides for individual projects, etc. I.e. not by restricting the expressiveness or convenience of the language itself. People can write this today:
yet, remarkably, they don't. A solid argument against the feature is if you believe that it's outright never a good idea to use it (as I think @chris-morgan might), in which case I would like to see some compelling evidence or reasoning to back that up. |
If you could destructure 'self', in the signature, that might remove some of the demand for this feature. but thanks to swift there's going to be a large community familiar with the .0 .1 ... notation One nice thing about tuples is avoiding needing to manage a dependancy. if two libraries use a common structure, which should own it ? - they can make themselves compatible without clashing on a declaration |
@chris-morgan |
Tuple structs (and tuple variants) are also annoying because they are very On Sun, Jul 27, 2014 at 11:49 AM, Kevin Ballard notifications@github.com
|
With Indexing traits finally on board, I've implemented matrices that index by tuples -- |
Yeah, in most cases you'll want a named field. But sometimes you don't. Most obvious case is multiple return values from a function; generally you'll want a tuple, not a struct. |
Indexing of tuples makes no sense because not all tuples are homogenous.
I agree with @chris-morgan.
Indeed but we can use patterns to name each values, as already noted in the Alternatives section. let (quot, rem) = num::div_rem(n, m);
println!("The remainder is {}.", rem);
// vs
let res = num::div_rem(n, m);
println!("The remainder is {}.", res.1); The latter is shorter but I prefer the former for readability. Also, an example by @kballard: let mut x = (1u, 2i, '3');
x.0 = 42u; It is cool but I prefer using variables and constructing a tuple when we need it (to pass to a function, as a return value, etc.) |
So do that. I'm not suggesting that you should be storing local values in a tuple. That snippet is not supposed to represent real code but instead supposed to demonstrate a reduction of real code. Fundamentally, a tuple is a heterogenous fixed-length collection. Similarly, an array is a homogenous fixed-length collection. Why should the latter allow indexing but not the former? |
@omasanori You’ve made me realise another point in favour of this RFC. If a function returns a tuple, quite often only one value of that tuple is needed. Your println!("The remainder is {}.", num::div_rem(n, m).1); This is obviously not a good example, because one can just use Now even if there aren’t many functions that return tuples (I don’t know how many there are), a feature like this could allow more functions that return tuples. Right now a function that returns a tuple can be quite difficult to use—one must assign each value in a Personally, I think that the best alternative (or even complement) to this RFC would be anonymous structs. They are a great way to give names to return values without having to define a whole new struct (std::str::CharRange is a good example of a struct which I consider unnecessary). However, unless we get anonymous structs, the best way to describe (in code) what values are in a tuple is to put the descriptions in the name of the function itself: |
@kballard Thank you for clarification. @P1start It sounds good but personally I still prefer let (_, rem) = num::div_rem(n, m); style. Indeed such shorthands must be loved by some people, though. Anonymous structs might be good too, |
Yes, that's why nobody is advocating for adding indexing syntax to tuples. The |
FWIW, I just attempted to use a tuple-struct containing a Sample code: struct TupleStruct<'a>(&'a mut String);
impl<'a> TupleStruct<'a> {
fn inner(&mut self) -> &mut String {
let TupleStruct(ref mut s) = *self;
// the following complains that the lifetime of `s` is too short
&mut **s
}
} Edit: Figured it out: struct TupleStruct<'a>(&'a mut String);
impl<'a> TupleStruct<'a> {
fn inner(&mut self) -> &mut String {
let TupleStruct(&ref mut s) = *self;
s
}
} |
+1 from me. |
I’ve made a functioning prototype implementation of this in my |
I've been wanting to propose something like this for a while. I much prefer |
Also: I'd rather not change the lexer to permit a.0.1. I'd rather just have that be an error and have people write out the names. We could always add it later. |
Discussed in https://github.com/rust-lang/meeting-minutes/blob/master/weekly-meetings/2014-08-26.md @P1start please ammend the RFC to remove the surface grammar that converts floats to field access (but do leave a note about the compromise). This is a complexity that we don't believe is worth contorting the grammar for. Also please add a feature gate ('tuple_indexing' perhaps). |
@brson I’ve amended the RFC. |
@P1start thanks! |
Merged as RFC 53. Tracking bug: rust-lang/rust#16950 |
I think adding a new syntax for this is unnecessary. This should work: let mut x = (1u, 2i);
let y = x[0];
x[0] = 2u; No need to add new syntax. But, the semantic checker must know that Pros:
Cons:
|
@asterite Indexing syntax was already commented upon, and rejected, because it's actually a really bad fit. Notably, indexing syntax everywhere else has a consistent type, but a tuple is heterogenous so |
Fixes an assertion that otherwise gets tripped. Closes rust-lang#184
Rendered view