Syntax for RangeFull: `..` #702

Merged
merged 3 commits into from Feb 3, 2015

Projects

None yet
@bluss
bluss commented Jan 21, 2015

Add the syntax .. for std::ops::RangeFull.

Range expressions a..b, a.. and ..b all have dedicated syntax and produce first-class values. This means that they will be usable and useful in custom APIs, so for consistency, the fourth slicing range, RangeFull, could have its own syntax ..

Rendered Version

@bluss
bluss commented Jan 21, 2015

Previously discussed here as well as discussions in #rust-internals. It has come up again due to deref coercions enabling &foo for many common "as_slice" use cases already, and because of ideas around using ranges in collection APIs.

I have already implemented this (draft impl) (on top of the PR rust-lang/rust#21374) it is a very simple change to make. dgrunwald's PR (Note: that has been merged) is also relevant because it fixes some grammar issues around the range syntax.

cc @gankro.

@Gankro
Contributor
Gankro commented Jan 21, 2015

I'd also like to add that .. is the only valid range to apply to a HashMap, if that's a thing we're interested in doing.

👍 from me, makes the language more consistent and opens up cool patterns.

@quantheory
Contributor

Note that there may be a few new edge cases. For instance, is &..-y equivalent to (&FullRange) - y or &(..(-y)) (or a syntax error)?

@blaenk
Contributor
blaenk commented Jan 22, 2015

Yes PLEASE. Thanks @bluss for writing this!

I really really really hope the team considers this. It's just much more consistent to have .. considering we already have a.., ..b, and a..b.

Previously the team seemingly decided against it stating that [] was shorter, but for the most prevalent (?) use cases we will now have simply &string and &vec thanks to deref coercions. I really hope the team is reasonable here. For types that want to encode range semantics, [] just looks completely irregular alongside the others.

@ghost
ghost commented Jan 22, 2015

👍 I'm in favor of this. It makes sense since for most use cases we will have an even shorter way of cross borrowing, like &string and &vector.

@P1start
Contributor
P1start commented Jan 22, 2015

👍 I agree with the RFC’s motivation for this: people are now beginning to recognise the use of ranges outside slicing alone: things like vec.remove(..) have been proposed recently. Because of that, having .. is not only more consistent, but more useful as well. Now that we have deref coercions, writing &foo[]/&foo[..] will probably be very rare, so the conciseness of full-range slicing doesn’t really matter. Full ranges are still potentially useful outside slicing alone, so to me, the most advantageous option is to use ...

@quantheory That particular case would be &(..(-y)) due to the precedence of .., but there might be some other less obvious cases.

@bluss
bluss commented Jan 22, 2015

It wasn't stressed enough in the RFC that we will probably want to use ranges a in future libstd API, it's not just about third party crates.

@dgrunwald
Contributor

@quantheory I don't think there are any new grammar edge cases, all the strangeness with this syntax can also be done with the existing a.. and ..a syntax.

@P1start: &..-y will actually be a syntax error with my PR: .. has a lower operator precedence than &; thus .. cannot be used as an operand to & without using parentheses, even if the expression could be parsed unambiguously when ignoring operator precedence. (this rule will prevent a lot of strange 'precedence reversal' cases that currently occur when combining .. with some other operators)

Without the &:
..-y will continue to be parsed as ..(-y) because .. has lower precedence than -. Note that this isn't new (x..-y is the same situation), and is not actually an ambiguity (parsing it as (x..)-y would go against the operator precedence).

@aturon aturon was assigned by nrc Jan 22, 2015
@kennytm
kennytm commented Jan 24, 2015

Note that existing code using .. in pattern matching will be broken when this RFC is merged:

enum Foo {
    A(i32, i32),
    B(u32, u32, u64),
}
fn main() {
    let k = Foo::A(1, 2);
    let q = match k {
        Foo::A(..) => 1,
        Foo::B(..) => 2,
    };
    assert_eq!(q, 1);
}

Examples:

This RFC should propose an alternate syntax of the above, after .. is changed to mean FullRange.

@bluss
bluss commented Jan 24, 2015

Very good catch. It doesn't break anything (range syntax is not parsed in patterns, only in expressions), but it's a confusing conflict. We'll have to discuss this issue.

@huonw
Member
huonw commented Jan 25, 2015

On patterns, we also have [a, b..] for slice patterns.

It wasn't stressed enough in the RFC that we will probably want to use ranges a in future libstd API, it's not just about third party crates.

e.g. #722 wants it.

@nikomatsakis
Contributor

Regarding patterns, I think that overlap already exists, it doesn't seem especially bothersome to me.

@tikue
tikue commented Jan 28, 2015

Strong +1 here. The consistency argument is compelling enough to me, but the fact that Ranges have identified use cases outside of indexing seals the deal.

@rkjnsn
Contributor
rkjnsn commented Jan 28, 2015

👍, for all of the reasons mentioned above.

@bluss
bluss commented Jan 30, 2015
  • Updated for the new name of the struct.
  • Updated text to mention the drawback of overlap with .. in patterns.
@bluss bluss changed the title from Syntax for FullRange: `..` to Syntax for RangeFull: `..` Jan 30, 2015
@quantheory
Contributor

I may as well point out that RFC 520 already states that .. in patterns is never interpreted as being a part of range syntax, since as @huonw mentioned there would have been an ambiguity with slice patterns otherwise. So this doesn't introduce much more of an inconsistency with patterns than we already had.

@bluss
bluss commented Jan 30, 2015

Slice patterns are more niche than using .. in patterns like this: match foo { Some(..) => though.

@quantheory
Contributor

Maybe. Personally, I've used slice patterns moderately often, because I work with various array-like types a lot. I use wildcards pretty rarely, in part because I typically use a pattern like Some(_) for variants that only hold one item.

@nrc nrc assigned nrc and unassigned aturon Feb 1, 2015
@nrc nrc merged commit 25d35f1 into rust-lang:master Feb 3, 2015
@nrc
Contributor
nrc commented Feb 3, 2015

Merged, r = @nikomatsakis, @huonw

Tracking issue: rust-lang/rust#21879

RFC: https://github.com/rust-lang/rfcs/blob/master/text/0702-rangefull-expression.md

This RFC has been accepted since it is a popular and minor improvement to the range syntax which has multiple use cases. Since we don't accept range syntax in patterns, we don't think there is any reason to worry about a clash of syntax there.

@bluss bluss referenced this pull request in rust-lang/rust Feb 4, 2015
Merged

Implement `..` syntax for RangeFull as expression #21947

@bors bors added a commit to rust-lang/rust that referenced this pull request Feb 5, 2015
@bors bors Auto merge of #21947 - bluss:full-range-syntax, r=brson
Implement step 1 of rust-lang/rfcs#702

Allows the expression `..` (without either endpoint) in general, can be
used in slicing syntax `&expr[..]` where we previously wrote `&expr[]`.

The old syntax &expr[] is not yet removed or warned for.
5e306de
@bors bors added a commit to rust-lang/rust that referenced this pull request Feb 6, 2015
@bors bors Auto merge of #21947 - bluss:full-range-syntax, r=brson
Implement step 1 of rust-lang/rfcs#702

Allows the expression `..` (without either endpoint) in general, can be
used in slicing syntax `&expr[..]` where we previously wrote `&expr[]`.

The old syntax &expr[] is not yet removed or warned for.
715f9a5
@caipre
caipre commented Feb 15, 2015

Regarding range syntax in patterns: we actually do accept ranges, but with ... rather than ... In patterns, the former allows a branch arm to match any value within a range while the latter can be used to ignore values when they won't be used in the match expression. Examples of each can be found in the Patterns chapter of the book.

I think it would be worth discussing whether we can swap the syntax here: .. should mean "range" inside patterns for consistency with the rest of the language, and ... reads well as an ellipsis, ie, "extra values ignored". Using ... as a placeholder for code also has precedent in Perl as the "yada yada" operator (though it's more akin to Python's pass than to Rust's current usage of .. in patterns).

Though the underlying objects / types may be different between ... ranges inside patterns and .. ranges outside of them, it seems to me that the semantics / intent is the same in both contexts.

Edit: It looks like the different meanings of .. are mentioned in RFCs 520 and 702. I don't see any further discussion though, and neither one discusses the ... syntax.

@bluss
bluss commented Feb 15, 2015

The discussion of inclusive range syntax is taking place, it's here!

@caipre
caipre commented Feb 15, 2015

@bluss : Thanks for the link. It seems like Niko explicitly requested not to discuss use of range syntax in patterns in that thread though:

I would prefer not to have this thread derailed with a generic discussion of whether .. or ... is preferable in other cases (e.g. patterns), nor the question of whether .. in patterns ought to destructure a range vs matching a range.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment