Slice
An array's length is part of its type, so a function taking an int32[5] accepts arrays of exactly five — no more, no fewer. A slice, written int32[..], removes that limit. It is a view into an array: where the elements start and how many there are, with no elements of its own. One function taking a slice serves arrays of every size, and making a view copies nothing.
A view, not a copy
A slice is two numbers — a position and a length — pointing into an array that already exists:
flowchart LR
subgraph numbers["`numbers: int32[5]`"]
direction LR
n0["`10`"] --- n1["`20`"] --- n2["`30`"] --- n3["`40`"] --- n4["`50`"]
end
middle["`middle = numbers[1..4]`<br/>starts at `20`, length 3"] -.-> n1The .. in int32[..] reads as "some number of": the element type is fixed, the count is not.
One function for every length
An array passed where a slice is wanted becomes a view of all of itself:
func Total(values: int32[..]) -> int32 {
var sum: int32 = 0;
for value in values {
sum += value;
}
return sum;
}
PrintLine("numbers {}", Total(numbers));
PrintLine("more {}", Total(more));
numbers has five elements and more has three, yet both calls reach the same function. Inside it, values has a length, can be indexed and can be walked with for, just like an array.
Part of an array
Indexing with a range gives a view of part of the array. As everywhere else, .. stops before its upper bound and ..= includes it, and either end may be left out:
| Expression | Elements | Meaning |
|---|---|---|
numbers[1..4] | 20, 30, 40 | indices 1 up to, not incl., 4 |
numbers[1..=4] | 20, 30, 40, 50 | indices 1 to 4 inclusive |
numbers[2..] | 30, 40, 50 | from index 2 to the end |
numbers[..2] | 10, 20 | from the start up to 2 |
numbers[..] | all five | the whole array |
A slice can be sliced again. Its indices count from the start of the view, not the array:
let inner = middle[1..];
middle starts at 20, so middle[1..] starts at 30, and inner[0] is 30.
Strings are slices
A string literal has been a slice all along: char8[..], a view of UTF-8 bytes. That is why .length counts bytes:
let greeting: char8[..] = "hello";
Read-only by default
A plain slice can look at the elements but never change them — Total cannot write into values. Writing through a view is the subject of the next lesson, Writable slice.
The program
The whole lesson is one package in the Examples repository. Its comments explain every step.
// An array's length is part of its type, so a function taking an `int32[5]` accepts arrays of
// exactly five. A slice, written `int32[..]`, removes that limit. It is a view into an array: a
// position and a length, with no elements of its own. One function taking a slice serves arrays
// of every size, and making a view copies nothing.
import Io::PrintLine;
// Every call below reaches this one function, whatever the length of what it is given.
func Total(values: int32[..]) -> int32 {
var sum: int32 = 0;
for value in values {
sum += value;
}
return sum;
}
func Main() -> int {
let numbers: int32[5] = [10, 20, 30, 40, 50];
let more: int32[3] = [1, 2, 3];
// An array passed where a slice is wanted becomes a view of all of itself.
PrintLine("numbers {}", Total(numbers));
PrintLine("more {}", Total(more));
// Indexing with a range gives a view of part of the array. As everywhere else, `..` stops
// before its upper bound, so this is elements 1, 2 and 3; `..=` would include the end.
let middle = numbers[1..4];
PrintLine("numbers[1..4] {} over {} elements", Total(middle), middle.length);
PrintLine("numbers[1..=4] {}", Total(numbers[1..=4]));
// Either end may be left out, meaning "from the start" or "to the end".
PrintLine("numbers[2..] {}", Total(numbers[2..]));
PrintLine("numbers[..2] {}", Total(numbers[..2]));
// A slice can be sliced again. Its indices count from the start of the view, not the array.
let inner = middle[1..];
PrintLine("middle[1..] {}, starting at {}", Total(inner), inner[0]);
// A string literal has been a slice all along: `char8[..]`, a view of UTF-8 bytes.
let greeting: char8[..] = "hello";
PrintLine("\"{}\" is {} bytes", greeting, greeting.length);
return 0;
}
Run it
cd Examples/Sequences/Slice
rux run
numbers 150
more 6
numbers[1..4] 90 over 3 elements
numbers[1..=4] 140
numbers[2..] 120
numbers[..2] 30
middle[1..] 70, starting at 30
"hello" is 5 bytes
Common mistakes
Try it yourself
- Write
Largest(values: int32[..]) -> int32and call it withnumbers,moreandnumbers[1..3]. - Print
Total(numbers[..])and check that it matchesTotal(numbers). - Write
CountAbove(values: int32[..], limit: int32) -> int32that counts the elements greater thanlimit, and call it onnumbers[1..]. - Try
numbers[3..1]and read the error.
Learn more
- Slices and Arrays as slices in the Rux Reference
- Using ranges — ranges as loop bounds and slice indices
- String literal — more on
char8[..]