# Slice range notation format

**URL:** <https://ziggit.dev/t/slice-range-notation-format/15299>\
**Category:** Brainstorming\
**Created:** [May 3, 2026, 2:11pm UTC](https://ziggit.dev/t/slice-range-notation-format/15299 "2026-05-03T14:11:24Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![uint](https://ziggit.dev/user_avatar/ziggit.dev/uint/32/7813_2.png) [@uint](https://ziggit.dev/u/uint)\
**Post date:** [May 3, 2026, 2:11pm UTC](https://ziggit.dev/t/slice-range-notation-format/15299/1 "2026-05-03T14:11:24Z")

</div>

# Preferred slice range notation format

- Allow specifying a start and an end index.

- For each start/end index: choose inclusive or exclusive.

- Omitted start or end defaults to the original array’s actual start or end.

## Examples

- `[1..4]` → slice from index 1 to index 4 (both inclusive).

- `[!1 .. !4]` → slice from index 2 to index 3 (exclude 1, exclude 4).

- `[..]` → entire array, from index 0 to the last index.

- `[! .. !]` → slice from index 1 to `max_index - 1` (exclude original first and original last element).

- All other combinations can be freely composed.

## Rationale

- The rule is explicit and requires no arbitrary memorization.

- It follows natural human reading intuition.

# Unwanted slice range notation format

- Dislike Python-style `[1..3]` design: left-inclusive but right-exclusive, which is counter-intuitive for human reasoning.

---

<div class="post-metadata">

**Author:** ![uint](https://ziggit.dev/user_avatar/ziggit.dev/uint/32/7813_2.png) [@uint](https://ziggit.dev/u/uint)\
**Post date:** [May 3, 2026, 2:30pm UTC](https://ziggit.dev/t/slice-range-notation-format/15299/2 "2026-05-03T14:30:38Z")

</div>

> [@uint](#):
>
> - No prefix → **inclusive**
> - Prefix `!` → **exclusive**

As for the choice of the prefix symbol, I think it can still be further discussed and refined.

---

<div class="post-metadata">

**Author:** ![Dok8tavo](https://ziggit.dev/user_avatar/ziggit.dev/dok8tavo/32/707_2.png) [@Dok8tavo](https://ziggit.dev/u/Dok8tavo)\
**Post date:** [May 3, 2026, 3:56pm UTC](https://ziggit.dev/t/slice-range-notation-format/15299/3 "2026-05-03T15:56:48Z")

</div>

I think using `<` for exclusive and `=` for inclusive is a lot more intuitive.

- inclusive-inclusive: `sliceable[start=..=end]`,
- inclusive-exclusive: `sliceable[start=..<end]`,
- exclusive-inclusive: `sliceable[start<..=end]`,
- exclusive-exclusive: `sliceable[start<..<end]`.

That being said, for slicing, inclusive-exclusive is vastly more useful than most, and I find it quite intuitive. I don’t even remember encountering a case were I needed an exclusive start.

---

<div class="post-metadata">

**Author:** ![IntegratedQuantum](https://ziggit.dev/user_avatar/ziggit.dev/integratedquantum/32/782_2.png) [@IntegratedQuantum](https://ziggit.dev/u/IntegratedQuantum)\
**Post date:** [May 3, 2026, 4:16pm UTC](https://ziggit.dev/t/slice-range-notation-format/15299/4 "2026-05-03T16:16:40Z")

</div>

> [@uint](#):
>
> left-inclusive but right-exclusive, which is counter-intuitive for human reasoning.

Out of the 4 possibilities, I’d argue it’s the most intuitive to work with.  
e.g. it has the property that `end - start` gives you the length, which is something that people mess up in real life when using other systems (e.g. to calculate the number of days given inclusive start and end dates, many people resort to counting them on their calendar)  
The only other system that has this property is left exclusive right inclusive, but that really makes no sense, since it would require -1 to slice things at 0.

> [@uint](#):
>
> The rule is explicit and requires no arbitrary memorization.

It would add a new symbol to memorize though, and it’s also easy to miss when reading, and easy to forget when writing.

---

<div class="post-metadata">

**Author:** ![pachde](https://ziggit.dev/user_avatar/ziggit.dev/pachde/32/2485_2.png) [@pachde](https://ziggit.dev/u/pachde)\
**Post date:** [May 3, 2026, 5:11pm UTC](https://ziggit.dev/t/slice-range-notation-format/15299/5 "2026-05-03T17:11:42Z")

</div>

There is already a standard notation for inclusive/exclusive ranges. The downside is that conventional coding editors assume balanced brackets and the standard notation mixes them. We just need better editors.

---

<div class="post-metadata">

**Author:** ![chrboesch](https://ziggit.dev/user_avatar/ziggit.dev/chrboesch/32/438_2.png) [@chrboesch](https://ziggit.dev/u/chrboesch)\
**Post date:** [May 3, 2026, 5:23pm UTC](https://ziggit.dev/t/slice-range-notation-format/15299/6 "2026-05-03T17:23:45Z")

</div>

> [@uint](#):
>
> Prefix `!` → **exclusive**

Then you had to write `for (0..!foo.len) |n| ..` which I don’t find very intuitive.

---

<div class="post-metadata">

**Author:** ![uint](https://ziggit.dev/user_avatar/ziggit.dev/uint/32/7813_2.png) [@uint](https://ziggit.dev/u/uint)\
**Post date:** [May 3, 2026, 5:46pm UTC](https://ziggit.dev/t/slice-range-notation-format/15299/7 "2026-05-03T17:46:54Z")

</div>

> [@uint](#):
>
> `[..]` → entire array, from index 0 to the last index.

if need to traverse all, just using this is OK;

---

<div class="post-metadata">

**Author:** ![pzittlau](https://ziggit.dev/letter_avatar_proxy/v4/letter/p/ecae2f/32.png) [@pzittlau](https://ziggit.dev/u/pzittlau)\
**Post date:** [May 3, 2026, 6:07pm UTC](https://ziggit.dev/t/slice-range-notation-format/15299/8 "2026-05-03T18:07:51Z")

</div>

The current state is to just do something like that;

```zig
const my_slice: []u8 = foo();
for (my_slice) |element| {
    // use element
}

```

Why would `[..]` be needed?

In general I’m unsure about what and if anything you’re trying to achieve with this. The `[inclusive..exclusive)` range syntax and specification is common and widely used and understood in _nearly all_ programming languages for reasons already explained above. The only major exception I can think about off the top of my head is [Julia](https://docs.julialang.org/en/v1/manual/arrays/#man-supported-index-types), which has inclusive bounds for both lower and upper.

---

<div class="post-metadata">

**Author:** ![theo](https://ziggit.dev/user_avatar/ziggit.dev/theo/32/8429_2.png) [@theo](https://ziggit.dev/u/theo)\
**Post date:** [May 3, 2026, 6:34pm UTC](https://ziggit.dev/t/slice-range-notation-format/15299/9 "2026-05-03T18:34:13Z")

</div>

It would be nice to have `..=` in Zig, but of course one can do without it.

---

<div class="post-metadata">

**Author:** ![truly-not-taken](https://ziggit.dev/user_avatar/ziggit.dev/truly-not-taken/32/6205_2.png) [@truly-not-taken](https://ziggit.dev/u/truly-not-taken)\
**Post date:** [May 4, 2026, 12:43am UTC](https://ziggit.dev/t/slice-range-notation-format/15299/10 "2026-05-04T00:43:41Z")

</div>

> [@pzittlau](#):
>
> The `(inclusive..exclusive]` range syntax and specification is common and widely used and understood

It is the other way around. The [[inclusive](https://www.mathwords.com/i/inclusive.htm), [exclusive](https://www.mathwords.com/e/exclusive.htm)) range syntax is what mathematicians use.

---

<div class="post-metadata">

**Author:** ![pachde](https://ziggit.dev/user_avatar/ziggit.dev/pachde/32/2485_2.png) [@pachde](https://ziggit.dev/u/pachde)\
**Post date:** [May 4, 2026, 4:07am UTC](https://ziggit.dev/t/slice-range-notation-format/15299/11 "2026-05-04T04:07:55Z")

</div>

And this would be fine syntax if not for dumb editors.

---

<div class="post-metadata">

**Author:** ![truly-not-taken](https://ziggit.dev/user_avatar/ziggit.dev/truly-not-taken/32/6205_2.png) [@truly-not-taken](https://ziggit.dev/u/truly-not-taken)\
**Post date:** [May 4, 2026, 4:25am UTC](https://ziggit.dev/t/slice-range-notation-format/15299/12 "2026-05-04T04:25:25Z")

</div>

> [@pachde](#):
>
> And this would be fine syntax if not for

… people who forget which one is inclusive and which one is exclusive. It is an obscure math notation that is not that common in programing languages.

```zig
foot(gun[42..69);

```

Can you fix the typo here? Did I miss `]` or `)`?

---

<div class="post-metadata">

**Author:** ![ericlang](https://ziggit.dev/user_avatar/ziggit.dev/ericlang/32/8515_2.png) [@ericlang](https://ziggit.dev/u/ericlang)\
**Post date:** [May 4, 2026, 10:29am UTC](https://ziggit.dev/t/slice-range-notation-format/15299/13 "2026-05-04T10:29:35Z")

</div>

for-loop ranges can be inconvenient.  
I just had one again.  
Imagine two `u6` vars `a` and `b` with which you want to compute something inclusive.  
In the loops there is a useless overflow danger, while we do **not** need the ` + 1` value.

```zig
fn calc(a: u6, b: u6) void {
     if (a > b) {
        for (b..a + 1) |v| {
        }
     }
    else if (a < b) {
        for (a..b + 1) |v| {
        }
    }
}

```

---

<div class="post-metadata">

**Author:** ![Sze](https://ziggit.dev/user_avatar/ziggit.dev/sze/32/496_2.png) [@Sze](https://ziggit.dev/u/Sze)\
**Post date:** [May 4, 2026, 10:55am UTC](https://ziggit.dev/t/slice-range-notation-format/15299/14 "2026-05-04T10:55:28Z")

</div>

If you use something like this:

```zig
fn calc(a: u6, b: u6) void {
    const l:u7, const u:u7 = if (a < b) .{a, b} else .{b, a};
    for (l..u + 1) |v| {
    }
}

```

There is no overflow danger because currently in zig `v` is a usize.  
But I would also like if we could have typed smaller indices like this:

> [@If you had one wish for zig, what would it be?](https://ziggit.dev/t/if-you-had-one-wish-for-zig-what-would-it-be/15069/20):
>
> For me it would also be for loop counting variable types, including the ability to do this: for (0..32) |i: u5| {} // max value does not have to fit the type

---

<div class="post-metadata">

**Author:** ![ericlang](https://ziggit.dev/user_avatar/ziggit.dev/ericlang/32/8515_2.png) [@ericlang](https://ziggit.dev/u/ericlang)\
**Post date:** [May 4, 2026, 10:59am UTC](https://ziggit.dev/t/slice-range-notation-format/15299/15 "2026-05-04T10:59:23Z")

</div>

yes… easily solvable when we typecast. easily solvable anyway but sometimes inconvenient.  
Typed looping vars would be great indeed.

---

<div class="post-metadata">

**Author:** ![Gaweringo](https://ziggit.dev/user_avatar/ziggit.dev/gaweringo/32/7550_2.png) [@Gaweringo](https://ziggit.dev/u/Gaweringo)\
**Post date:** [October 27, 2026, 10:39pm UTC](https://ziggit.dev/t/slice-range-notation-format/15299/16 "2026-10-27T22:39:41Z")

</div>

Adding to this somewhat old thread..  
I personally am generally fond of clearer indications of inclusive and exclusive ranges like `..<` and `..=`.

Something that could also be nice in the realm of slice syntax would be the ability to specify start + length. Something like `[start..+10]`. Thought of this while looking through this thread: [Reading integers from dynamic address - #2 by dbushell](https://ziggit.dev/t/reading-integers-from-dynamic-address/8927/2)

Something else that is related to slicing/ranges, which I know from VHDL, but might also be available in Ada (or others), is the ability to use ranged integer types as slices/ranges, which would require [#3806](https://github.com/ziglang/zig/issues/3806).

As an example from VHDL where a new integer subtype with a range from 0 to 255 is declared, and used to specify the range / size of an array type:

```vhdl
subtype ram_addr_t is natural range 0 to 255;
type ram_t is array(ram_addr_t) of word_t;
signal ram : ram_t;

```

The integer type can also be used to take a slice from an array:

```vhdl
subtype opcode_range_t is natural 31 downto 26;
subtype instruction_t is bit_vector(31 downto 0);
signal instruction : instruction_t;
-- Later ...
opcode := instruction(opcode_range_t); -- ( ) are used to index into arrays

```

---

<div class="post-metadata">

**Author:** ![KilianHanich](https://ziggit.dev/user_avatar/ziggit.dev/kilianhanich/32/8254_2.png) [@KilianHanich](https://ziggit.dev/u/KilianHanich)\
**Post date:** [October 28, 2026, 1:24am UTC](https://ziggit.dev/t/slice-range-notation-format/15299/17 "2026-10-28T01:24:23Z")

</div>

Why shouldn’t we just copy Odin with `..<` and `..=`?  
These at least look somewhat intuitive with their meaning.

---

<div class="post-metadata">

**Author:** ![vincentd](https://ziggit.dev/user_avatar/ziggit.dev/vincentd/32/9870_2.png) [@vincentd](https://ziggit.dev/u/vincentd)\
**Post date:** [October 28, 2026, 5:48am UTC](https://ziggit.dev/t/slice-range-notation-format/15299/18 "2026-10-28T05:48:36Z")

</div>

I have heard it said that programming never has any new ideas, just old ones and a `while()` loop.

> **[E.W. Dijkstra Archive: Why numbering should start at zero (EWD 831)](https://www.cs.utexas.edu/~EWD/transcriptions/EWD08xx/EWD831.html)**

> Remark The programming language Mesa, developed at Xerox PARC, has special notations for intervals of integers in all four conventions. Extensive experience with Mesa has shown that the use of the other three conventions has been a constant source of clumsiness and mistakes, and on account of that experience Mesa programmers are now strongly advised not to use the latter three available features. I mention this experimental evidence —for what it is worth— because some people feel uncomfortable with conclusions that have not been confirmed in practice. (End of Remark.)

In modern terms: it’s a footgun.

---

<div class="post-metadata">

**Author:** ![gwenzek](https://ziggit.dev/user_avatar/ziggit.dev/gwenzek/32/8137_2.png) [@gwenzek](https://ziggit.dev/u/gwenzek)\
**Post date:** [October 28, 2026, 7:02am UTC](https://ziggit.dev/t/slice-range-notation-format/15299/19 "2026-10-28T07:02:57Z")

</div>

Only thing I really want is `x[2..-1]` I’m fine if it only works with numeric literals.

---

<div class="post-metadata">

**Author:** ![npc1054657282](https://ziggit.dev/user_avatar/ziggit.dev/npc1054657282/32/5790_2.png) [@npc1054657282](https://ziggit.dev/u/npc1054657282)\
**Post date:** [October 28, 2026, 7:33am UTC](https://ziggit.dev/t/slice-range-notation-format/15299/20 "2026-10-28T07:33:59Z")

</div>

I used to think both of Odin’s representations were pretty good. But after thinking about it a bit more, now I hope that no matter which one is used, there’s at least only one range syntax.

I agree that both the left-closed right-open and left-closed right-closed approaches have their advantages, but they also have their own pitfalls. In my view, their pros and cons almost always exist, so choosing one basically means accepting its problems for the sake of its benefits. That’s why I think mixing them is always worse, because then you constantly have to be aware of which one you’re using and watch out for the potential footgun of each approach.

The advantages of left-closed right-open intervals are clearly explained by Dijkstra, but their drawbacks are also obvious. If you want to represent a range of n-digit numbers, you have to introduce n+1-digit numbers. Otherwise, you might run into overflow footguns.

Zig’s approach is to require that all ranges must be `usize`. Obviously, this isn’t the most pleasant approach, as it forces you to use a lot of annoying `@intCast` and you can’t use negative numbers. But after paying that price, we get left-closed, right-open range that feel intuitive to programmers, along with all the advantages that Dijkstra praised. Since `usize` is huge enough that you basically can’t traverse it completely, this hacky method effectively avoids overflow risks.

Using closed intervals on both ends avoids overflow issues, but it sacrifices all the benefits that Dijkstra praised and goes against programmer intuition. Almost all modern programming languages use left-closed, right-open intervals, which has become the norm, and going against it will definitely cause more bugs.

But anyway, after thinking it over, I still believe there should only be one way to represent a range, no matter what flaws it has.

Maybe there’s a win-win solution: using the left-closed, right-open approach, while also being able to automatically handle overflow issues. Hopefully [#3806](https://github.com/ziglang/zig/issues/3806) can make this happen.

[Next page](https://ziggit.dev/t/slice-range-notation-format/15299.md?page=2)
