`...` vs `..=` for inclusive ranges

Consider ..,: while it’s not as distinct as ..=, it’s more recognizeable than ..., and it doesn’t look too ugly at first, and there’s a little mnemonic behind it that makes it appealing:

1..,9 ::= 1..9, 9

It’s sort of like gcc’s truncated ternary operator. This should make it the obvious choice ^,~

4 Likes

Great suggestion @scythe. 1..,9 makes it very clear that 9 is the last element in the range, because of the , before it.

It’s incompatible with useful syntax like (1.., 1.., 2..) i.e. a tuple of ranges.

Oops. Then I‘d say I prefer the status quo.

1 Like

I personally would like to see something like this:

match i {
    [0..10] => ..., // would be 0 up to and including 10
    (0..10] => ..., //  would be 1 up to and including 10
    (0..10) => ..., // would be 1 up to and including 9
    [0..10) => ..., // would be 0 up to and including 9
}

But I feel like it would be to close to existing syntax.

1 Like

I’m most fond of (0..10).inclusive(), but that’s useless in a pattern. In general it follows Rust’s idea of composition wherever possible.

Unfortunately this is a non-starter in the parser because of unbalanced parens.

I've felt very strongly about range syntax in Rust, for some reason. I had all but given up on even switching from [] to [..] since the team seemed intransigent on the issue, so I was very excited and surprised when I saw that Niko seemed to be OK with @bluss' RFC. I figured we were on the right track for adding ... soon after. Now I'm afraid of it being derailed by some people who just love to participate in endless bikeshedding for its own sake.

  1. I strongly disagree that it's difficult to remember. a..b is 'a up to but not including b' a...b with its additional dot means 'go one more', i.e. do include b. I think once people associate it this way, it should be very difficult to forget.
  2. I really like Ruby, but I don't think we should let its mistakes impact the development of Rust.
  3. This one I'm more sympathetic to, but given #1, I really don't think it would be an issue.
  4. I'm somewhat sympathetic to this issue, but as many have stated, it really stands out in your typical monospace font. In fact, given this, it should make #3 much less likely.

As others have said, a..=b is pretty ugly. The only alternative syntax for ranges that I could think of that would feel somewhat natural is the one swift used for exclusive ranges: a..<b, because it reads as a range up to but "less than b", but I agree with Niko that exclusive ranges are more common, so it doesn't make sense to make them be the ones to stand out (and more typing, even if it's just one letter). I understand that the intention is the same with a..=b, but the equal sign = is just so loaded that it actually detracts from the connotation of "ranges" completely, in my opinion. These quotes from this post seem to agree:

I'm really hoping we pull through here with a..b and a...b because it's much more natural, in my opinion.

7 Likes

Just wanted to add my 2c. I was redirected here by someone else after creating an issue on Github for the specific syntax this discussion addresses.

I don’t have any opinion on whether “…” or “…” should be inclusive/exclusive. I leave that issue to other people with stronger opinions.

I do however think that the currently separate use-cases for the syntaxes should overlap more than they currently do, i.e. we should be able to use inclusive range globally, and exclusive ranges in match patterns. Either than or abolish one of the syntaxes and extend the other to replace the one that is abolished. It’s not hard to ±1 to convert an inclusive/exclusive syntax to an exclusive/inclusive one.

If both .. and ... are allowed, it needs to be guaranteed, that the RangeFrom struct will never become tuple-struct. Otherwise 5...6 might be ambiguous to accessing the 7th tuple field of the RangeFrom struct.

The quote is ambiguous. In my experience with Mesa, its range syntax was straightforward, but you do have to decide which kind of range to use when. Was that Dijkstra's point? (Write [2..12] or [2..length) depending on the situation.) Or maybe I didn't know about or forgot about range mistakes other people made.

The Mesa notation is so memorable that I still use it in comments, [a..b) or (a..b) or [a..b] or (a..b]. I find it disorienting when others use what I guess is a more recent math notation (a,b) or [a,b]. That reads as tuples or vectors.

Truthfully, the symmetrical notation a..b is not great for the asymmetrical half-open interval [a..b). We can cope, but I anticipate more mistakes than Dijkstra refers to.

(a..b).inclusive() adds a second asymmetry by modifying only one side, using a name that doesn't take sides.

a..=b is an asymmetrical notation for a symmetrical closed interval. (That is, if you read it as an interval. It sure looks like an assignment to me.)

a...b may be OK, but does 1...2 mean 1 ... 2 or 1.0 .. 2, or 1 .. 0.2?

2 Likes

-1 for anything that complicates the “Range” types, it makes all code that uses them unnecessarily complicated and less performant.

There are two problems that bother me with current syntax:

  1. Symmetrically looking operator .. is used to express “assymetric” range (left-inclusive, right-exclusive). That feels a little arbitrary.
  2. The .. operator is used in some other languages to express inclusive range – eg. in Haskell. I have to stop and think every time I write .. in any of those two languages. (I am aware that there exist languages which use .. with same semantics as Rust, but that doesn’t invalidate this problem. And from what I know ... operator is free of this issue, as it’s always used for inclusive ranges)

Replacing ... with ..= solves none of these problems and additionaly is grammatically ambiguous, and also makes symmetric range expressed with assymetrically looking operator.

I’d be happy with a solution that has none of these two problems. Swift’s may be a good example. I agree it’s not the prettiest one, but I think ... and ..< ranges are obvious from the first sight¹ (even if you see just one of these). It is also non-ambigous (assuming we’d use ... also for unbounded ranges) and extendable (by >.. and >.<). I think that it’s better to pay additional keystroke than to have to think every time .. is used or spend time debugging.


  1. @ker seems to want ..< for right-inclusive range, so maybe it’s not as obvious as I thought before. Or maybe it was just a typo?

Edit: I was confused about Ruby syntax working the other way.

2 Likes

I like the current syntax for basically all the reasons that are mentioned here. ..= is ugly and it doesn’t suggest ‘range’ to me either.

1 Like

The tokeniser already parses ... as its own token in that context (try stringify!(1...2) and note the spacing), so 1...2 would be parsed as 1 ... 2. This problem already exists for 1..2 anyway (it could be parsed as (1.).2, which is valid syntactically but nonsensical semantically).

2 Likes

+1 for: a...b for [a, b] a..<b for [a, b) a>..b for (a, b] a>.<b for (a, b)

But I guess I’d be fine with the following as well: a...b for [a, b] a.. b for [a, b) a>...b for (a, b] a>.. b for (a, b)

1 Like

I prefer ... (“more dot, more number”, fine!)

(Another candidate: a..b+, … is a bit ugly/weird too)

1 Like

a … b for [a, b] a …^ b for [a, b) a ^… b for (a, b] a ^…^ b for (a, b)

^ for exclusive

-1, I think “…” and “more dots more numbers” are fine.

In a monospaced font, two and three dots are pretty easily recognizable.

And from a “what would you guess this line of code does” perspective, both “…” and “…” strongly suggest to me “range” or “sequence”, due to the use in math and English.

That said, I could imagine some value in a syntax that allowed expressing open, closed, and half open intervals.

2 Likes

Is there even any need for ranges exclusive on the first element (a>...b and a>.. b as @tommit wrote)? The only use case occurring to me is reverse iteration, but using something like (a..b).reverse() or myseq.riter() (similar to C++'s rbegin() and rend()) would seem the more sensible way to go for this.