Rules / Operators / postcircumfixes

Positional subscript Written

Indexing and slicing by position — one operator that returns an element, a list, or the whole thing depending on what you put inside it.

[ ]
postcircumfixmethod call 2/27left-assocRaku++ parses this — execution uncheckeddocs.raku.org ↗

@a[…] looks up by position. What comes back depends entirely on the shape of what is inside the brackets, and the bracket contents are ordinary Raku code evaluated in list context — not a special index syntax.

This is postcircumfix:<[ ]>, distinct from [circumfix:<[ ]>](/rules/operators/circumfix-brackets/), which builds an array. The difference is whether a term precedes the bracket.

Rules #

R1 One index returns one element

my @a = 10, 20, 30;
say @a[1];
Output
20

R2 More than one index returns a list — a slice

The result is a List, in the order you asked for, repeats included. It is not a copy of a range of the array; it is the elements you named.

my @a = 10, 20, 30;
say @a[0, 2];
say @a[1, 1, 0];
Output
(10 30)
(20 20 10)

R3 The subscript is evaluated in list context, so anything list-like indexes

A range, a sequence, a ^n, the result of a method call — all of them flatten into the index list. There is no separate "slice syntax"; there is only list context.

my @a = 1 .. 10;
say @a[2 .. 4];
say @a[^3];
say @a[(1, 2).map(* * 2)];
Output
(3 4 5)
(1 2 3)
(3 5)

R4 * inside a subscript means "the number of elements"

*-1 is not a negative index — it is a WhateverCode closure that the subscript calls with the element count. That is why it composes with arithmetic.

my @a = 10, 20, 30;
say @a[*-1];
say @a[*-1, *-2];
Output
30
(30 20)

R5 An empty subscript is the whole thing — the zen slice

@a[] returns the container itself, not a copy and not a flattened list. It is how you ask for "everything" without changing the shape.

my @a = 10, 20, 30;
say @a[];
Output
[10 20 30]

R6 Reading past the end returns Any and does not grow the array

my @a;
say @a[3].WHAT;
say @a.elems;
Output
(Any)
0

R7 Assigning past the end does grow the array, filling with Any

The asymmetry is deliberate: reading is free, writing commits.

my @a;
@a[3] = 1;
say @a.raku;
Output
[Any, Any, Any, 1]

R8 A slice is assignable

my @a = 1, 2, 3;
@a[0, 1] = 9, 8;
say @a;
Output
[9 8 3]

R9 Adverbs select what the subscript returns

The subscript takes adverbs that change the result from the value to something about the value: :exists, :delete, :k (keys), :v (values), :p (pairs).

my @a = <a b c>;
say @a[2]:exists;
say @a[9]:exists;
say @a[0, 1]:kv;
Output
True
False
(0 a 1 b)

:delete returns what was there and leaves a hole, not a shorter array:

my @a = <a b c>;
say @a[1]:delete;
say @a.raku;
Output
b
["a", Any, "c"]

R10 Booleans index, because Bool is an Int

True is 1 and False is 0, so a boolean subscript is a perfectly ordinary numeric one. Occasionally useful, more often a bug.

my @a = 10, 20, 30;
say @a[True, False];
Output
(20 10)

R11 Subscripting a scalar item yields the item itself at index 0

Any value is a one-element list when asked. This is why %h[0] gives you the hash rather than an error, and why $s[0] is the whole string rather than its first character.

my $s = "abc";
say $s[0];
my %h = a => 1;
say %h[0];
Output
abc
{a => 1}

For a character, index the .comb list — $s.comb[0] — or use .substr(0, 1).

Traps #

T1 Negative indices are not "from the end"

@a[-1] is an error, not the last element. Raku spells that @a[*-1]. Rakudo rejects it at compile time with a message pointing you at *-1; Raku++ accepts the parse and throws X::OutOfRange when the subscript runs. Either way the code is wrong — but the two implementations tell you at different moments.

my @a = 1, 2, 3;
say @a[-1];

T2 A single list subscript flattens; a list inside the subscript does not

One list in the brackets is just an index list:

my @a = 10, 20, 30;
say @a[(0, 1)];
Output
(10 20)

But a list nested among other indices is a different matter, and this is where the two implementations part company. Rakudo mirrors the shape of the subscript into the result, so @a[0, (1, 2)] returns a two-element list whose second element is itself a list:

engines differ
my @a = 10, 20, 30;
say @a[0, (1, 2)];
Rakudo:  (10 (20 30))
Raku++:  (10 20 30)

Raku++ flattens. If the shape matters, do not rely on the subscript to preserve it — build it explicitly. To index into a nested array, chain the subscripts (@a[0][1]) or use the semicolon form below.

T3 ; inside a subscript is the multi-dimensional form, and Raku++ differs on flat arrays

@a[0;1] indexes dimension by dimension. On a genuinely nested array both implementations agree:

my @a = [1, 2], [3, 4];
say @a[0; 1];
Output
2

On a flat array, Rakudo lets a trailing dimension degenerate — @a[1;0] gives @a[1] — while Raku++ returns Any. Treat ; on a one-dimensional array as undefined territory rather than relying on either answer.

engines differ
my @a = 1, 2, 3;
say @a[1; 0];

T4 Binding a slice gives different types in the two implementations

my @b := @a[0, 1] binds a List in Rakudo and an Array in Raku++. The values match; .WHAT and mutability do not. Assign (=) instead of bind (:=) unless you specifically need the binding.

engines differ
my @a = 1, 2, 3;
my @b := @a[0, 1];
say @b.^name;

See also #