Arrays and slices
An array holds a fixed number of values of the same type one after another, and you reach each of them by its position. Arrays are the simplest and fastest way to keep a list whose length you know in advance.
Building and reading an array
Build an array with square brackets. Positions start at zero, and len() gives the number of elements. To change elements, declare the array mut. println prints a whole array at once, and an array may itself hold arrays, as table does here.
func main:
mut numbers := [5, 3, 8, 1]
println numbers[0], numbers[3], len(numbers)
numbers[1] = 90
println numbers
table := [[1, 2, 3], [4, 5, 6]]
println table[1][2]
endThe type of numbers is int[4]: the length is part of the type and never changes. When you need a list that grows, use a Vector from the collections lesson.
An array of a given size
When you know the length but not yet the values, declare the array with its type: mut votes: int[4]. Numbers start at zero and bool elements at false. Counting things by number is the classic use:
func main:
mut votes: int[4]
ballots := [2, 0, 2, 3, 2, 0]
each b in ballots:
votes[b] += 1
end
println votes
each option, count in votes:
println "option", option, "got", count
end
endDo not read the elements of a str array declared this way before you have set them: they hold no string yet, and reading one crashes the program. Build a str array from a literal instead, such as ["", "", ""].
Walking an array
each item in array visits the elements in order. With two names, each i, item in array, you also get the position.
func main:
fruits := ["apple", "pomegranate", "grape"]
each fruit in fruits:
println "I like", fruit
end
println "count:", fruits.len()
endSearching and adding up
Most work on an array is one loop that carries a result along: a running total, the position of the largest value so far, or the first position that matches. Keep the position rather than the value when you will need both, as with wettest below. A search function returns -1 when nothing matches, a value that cannot be a real position.
func index of(items: str[], wanted: str): int:
each i, item in items:
if item == wanted:
ret i
end
end
ret -1
end
func main:
months := ["Jan", "Feb", "Mar", "Apr", "May", "Jun"]
rain := [78, 64, 51, 33, 12, 4]
mut total := 0
mut wettest := 0
each i, mm in rain:
total += mm
if mm > rain[wettest]:
wettest = i
end
end
println "total:", total, "average:", total / len(rain)
println "wettest:", months[wettest], rain[wettest]
println "Apr is month", index of(months, "Apr") + 1
println "Dec found at", index of(months, "Dec")
endTwo arrays of the same element type and length compare with == and !=: they are equal when every element is equal, so [1, 2, 3] == [1, 2, 3] is true. Arrays of different lengths cannot be compared, and neither can arrays with < or >; both are error E021.
Two dimensional arrays
An array of arrays is a table: sales[r][c] is row r, column c. len(sales) is the number of rows and len(sales[0]) the number of columns. An array literal may span several lines, which keeps a table readable. each does not walk a two dimensional array (E065), so use repeat with indexes. A single row, sales[r], is an ordinary array and can be handed to a function.
func total(values: int[]): int:
mut sum := 0
each n in values:
sum += n
end
ret sum
end
func main:
sales := [
[12, 7, 3, 9],
[5, 11, 8, 6],
[9, 4, 10, 2]
]
shops := ["north", "south", "east"]
repeat len(sales) in r:
println shops[r], total(sales[r])
end
mut days := ""
repeat len(sales[0]) in c:
mut sum := 0
repeat len(sales) in r:
sum += sales[r][c]
end
days += " " + sum
end
println "per day:" + days
endTo declare an empty table, give both sizes: mut grid: int[3][4].
Arrays are values
Assigning an array to another variable copies all of it. So does passing it to a parameter with a fixed size such as int[3], and so does returning one. Changing the copy never touches the original.
func with bonus(scores: int[3]): int[3]:
mut out := scores
repeat len(out) in i:
out[i] += 5
end
ret out
end
func main:
original := [70, 85, 90]
mut draft := original
draft[0] = 0
println original, draft
curved := with bonus(original)
println original, curved
endThat makes arrays safe to pass around, but copying a large array on every call costs time. When a function should only read an array, or should change the caller's array, give it a slice instead.
Slices
array[start: end] makes a slice over the elements from start up to but not including end. It is not a copy: it points at the same data. [: 2] takes from the beginning and [4:] runs to the end. A parameter of type int[] accepts any array or slice of ints, whatever its length, and an array passes to it directly, with no copy.
func sum(view: int[]): int:
mut total := 0
each n in view:
total += n
end
ret total
end
func main:
data := [1, 2, 3, 4, 5, 6]
middle := data[1: 4]
println len(middle), sum(middle)
println sum(data), sum(data[: 2]), sum(data[4:])
endSlices write through
Because a slice points at the caller's data, a function that changes elements of an int[] parameter changes the caller's array. Pass a slice of part of the array and only that part changes.
func discount(prices: int[], percent: int):
repeat len(prices) in i:
prices[i] -= prices[i] * percent / 100
end
end
func main:
mut prices := [200, 150, 80, 60, 40]
discount(prices[3:], 50)
println prices
discount(prices, 10)
println prices
endA function that returns a slice must not return one that points into its own local array, since that array is gone once the function returns; the compiler rejects it (E012).
Index errors
Reading or writing past the end of an array is always a mistake, and Salam catches it. When the index is a constant, the build stops: a[3] on a three element array gives "index 3 is out of bounds for an array of length 3" (E021). When the index is only known at run time, the program stops with "array index 5 out of bounds [0,3)" instead of reading memory that is not part of the array.
A build with --release leaves out these run time checks to save time, so an index error there is no longer caught. Test without --release first.
Rules of thumb
- Use an array when the length is known and fixed, and a
Vectorwhen it is not. - Take an array parameter as
int[](a slice) unless the function needs its own copy. - Remember that a slice writes through to the array it came from.
- Walk a two dimensional array with
repeatand two indexes. - Return
-1from a search that finds nothing, and check for it.