A `range!´ macro


#1

Right now range is not extremely useful—it can yield an iterator from a to b (exclusive), and that’s it. Quite often more complex ranges are needed—ones that have no upper limit, ranges that need to be inclusive of the upper bound, steps other than 1, and so on. Macros are great for this! It’s quite simple to design a nice range macro:

macro_rules! range {
    (.. $to: expr) => {
        std::iter::range(std::num::zero(), $to)
    };
    (... $to: expr) => {
        std::iter::range_inclusive(std::num::zero(), $to)
    };
    (.. $to: expr, $step: expr) => {
        std::iter::range_step(std::num::zero(), $to, $step)
    };
    (... $to: expr, $step: expr) => {
        std::iter::range_step_inclusive(std::num::zero(), $to, $step)
    };
    ($from: expr .., $step: expr) => {
        std::iter::count($from, $step)
    };
    ($from: expr .. $to: expr) => {
        std::iter::range($from, $to)
    };
    ($from: expr ... $to: expr) => {
        std::iter::range_inclusive($from, $to)
    };
    ($from: expr ..) => {
        std::iter::count($from, std::num::one())
    };
    ($from: expr .. $to: expr, $step: expr) => {
        std::iter::range_step($from, $to, $step)
    };
    ($from: expr ... $to: expr, $step: expr) => {
        std::iter::range_step_inclusive($from, $to, $step)
    };
}

Playpen link.

This takes into account basically every type of range/counter I can think of, and it’s also got a very nice syntax (although the .. vs. ... distinction could be confusing, but Ruby uses it too IIRC). If this were added to std::macros, ranges would be a lot nicer to construct.

Thoughts?


#2

For those who are happy using macros but do not like reading them, here is an alternative playpen link that shows a larger set of concrete examples:


#3

The basic rules for his macro (like @pnkfelix’s post, for easier comprehension) :

// `range!(a..b)` for example
//
// `a..b` => [a,b) (exclusive)
// `a...b` => [a,b] (inclusive)
// `..b` => [0,b)
// `a..` => [a, infinity)
// `a..b, c` => [a,b) in increments of c

#4

A strong +1 for me, this is exactly what macros are meant for. I particularly like the increment notation. The choice of ... vs .. seems arbitrary (and in fact a reverse of what it is in Ruby), but I think it will not be a problem in practice. My only (minor) worry is that ... is not a token, so range!(a.. . b) will be an accepted syntax, but it’s not the first time this would happen in Rust (range/*hi there*/!(a..b) is also accepted).


#5

It is a token, and that is not excepted syntax (at least, trying to use it on playpen gives unexpected token messages).


#6

Ah, well even better then. The design is perfect!


#7

Why not borrow an idea from Swift and use …< instead of …, to avoid confusion?


#8

I don’t think the parser can handle that. A quick modification seems to suggest that macros aren’t powerful enough, and in any case I think it’s ambiguous (or at least would require infinite lookahead) with UFCS (where <T>::f() is a valid expression).


#9

The very minute difference between ... and .. troubles me, though. I’d have to squint and think extra carefully to be sure I’m not introducing a bug.


#10

I think ..< is too ugly for such a common range operation.

That said, using .. could be confusing as well in Rust because

match x {
    0..10 => something,   // this is inclusive.
    _ => something_else,
}

#11

FWIW, reordering the rules so ..< is before ..expr is enough to make that work. Of course, this doesn’t address ambiguity or ugliness. And I guess it would be pretty annoying to type, especially if you’re like me and use thumbs exclusively for both . and <

As a non-Rubyist, though, I think I’d be fairly likely to mistake whether something is inclusive or exclusive while skimming. I guess I can always stick with range(0, x).


#12

To eliminate any ambiguity, couldn’t Rust just take the stand that ALL ranges are inclusive? If you need exclusive, just subtract 1.


#13

The match syntax would already conflict with the proposed slicing syntax sugar. I think it would be simple to fix by simply making the match syntax use ....


#14

On Sat, Aug 16, 2014 at 07:29:28PM +0000, seanmonstar wrote:


#15

Test post (try top-posting, maybe discuss can’t handle inline responses?)


#16

I like this very much, although I do think .. should be [a,b], and ... [a,b). It’s consistent with match and with Ruby/Perl.


#17

Ooh, yes! I’d much rather have the standard mathematical range notation!

I guess [x], [x) would be the (..x) (...x) variants. We could have (x] and (x), too. The step notation would be a bit more awkward, though.


#18

Rust syntax and macro syntax does not allow unmatched parentheses or brackets.