Note: All of this capability is implemented except .len, multi-element segments, 'each' iteration, comparison, and pattern matching.
An array holds a specified number of values, all of the same type. The size of the array (its number of elements) is a fixed, constant value and is not resizable. The array's elements are located next to each other in memory. Elements are individually accessed using an unsigned integer index.
For example:
imm numbers [3; f32] = [3.1, 4.1, 5.92] imm nbr = numbers[1] // nbr is 4.1 numbers[0] = 2. // numbers is now [2., 4.1, 5.92]
Here, numbers is an array containing three floating-point numbers as its elements. As the redundant type specification [3; f32] indicates, the length of the array is 3 and the type of its elements is f32. The [] operator enables access to a specific element.
Arrays are always one-dimensional. However, it is possible to achieve a multi-dimensional capability by embedding an array within an array:
imm matrix [3; [3; f32]] = [[1., 0., 0.], [0., 1., 0.], [0., 0., 1.]] imm val = matrix[1][1] // 1.
Warning: Typically, when you want a collection type capability, you won't reach directly for an array, because of the fixed-size limitations and because you cannot implement methods directly on arrays. More likely, you will use one of the library-based collection types. If you look under the covers of these, you will see these are basically struct types that wrap around a specific array or array reference (as well as possibly other fields). Being structs, they offer implemented methods to work with the collection and its elements.
Initialization
To create or initialize an array, specify the values of all its elements separated by commas and enclosed within square brackets (as the above example demonstrates). When this array constructor is used to initialize a global variable, the element values must be constants. When used as part of a function's logic, element values may be specified using expressions:
imm pair = [nbr * nbr, nbr + nbr]
An alternative way to create an array is to specify it as a string:
imm hello = "Hello world!" // type: [12] u8
A string is encoded as an sequence of utf-8 code points. The length is the number of bytes it occupies. The type of each element is u8. For C-compatibility, the zero terminator ('\0') is placed at the end of the string, but not counted in its length.
Fill Initialization
An array can be initialized with a single fill value by using a semi-colon to separate the array's length from its fill value. This example creates a 100-byte string, where all bytes are spaces:
imm spaces = [100; ' ']
Copying
Like numeric values, array values may be passed around between variables and functions:
imm hello = "Hello" fn main(): mut dessert = jello(hello) fn jello(mut saying [5; u8]) [5; u8]: saying = "jello" saying
In this example, a copy of the hello variable's array is passed to the function jello. It replaces that copy's contents with the string "jello" and returns it to main. The mutable local variable dessert now holds a copy of the array string "jello".
When arrays are passed around, we always make copies of the array (just like we do with numbers). Any changes made to a copy won't change the original array.
This may not always be the behavior that you want, though. If instead you want to pass around a reference to an array, such that any changes do change the original array, obtain and pass an array reference.
Getting the length
To get a count of how many elements are in an array, use the len method:
imm hello = "Hello" imm length = hello.len // =5
Indexing
A single element within an array may be accessed using an unsigned integer index value enclosed in square brackets. The specified index may be an expression. An index of 0 refers to the first element.
Indexing can be used to get or set the specified element:
mut c_array = "coal" c_array[2] = c_array[1] // the 3rd element is now the same as the second // c_array now holds the array value "cool"
Array indexing is automatically bounds-checked by its known size. Any attempt to supply an invalid index results in a compile-time or run-time error.
Note: It is also possible to use the ampersand operator to borrow a reference to a specific indexed element in an array.
Indexing from the end
Sometimes it is more convenient to index an element based on its position relative to the end of the array. This can be accomplished in a verbose way:
imm nbrs = [2, 4, 6] imm four = nbrs[my.len - 2] // =4 - the 2nd from the end
The less verbose equivalent uses the $- operator:
imm four = nbrs[$-2]
$- is equivalent to my.len -
Iterating over an array
Using indexing and the len method, one can now easily iterate over every element in an array:
imm numbers = [3.5, 4.0, 5.5, 3.0] average = 0.0 mut i = 0 while i < numbers.len: average += numbers[i++] average /= numbers.len // == 4.0
A cleaner, more concise approach uses an each block instead:
imm numbers = [3.5, 4.0, 5.5, 3.0] average = 0.0 each nbr in numbers: average += nbr average /= numbers.len // == 4.0
Multi-element copying
Multiple elements of an array may be copied into another array using a range index.
mut greetings = "Hello Jeter" greetings[7,4] = greetings[1,4] // "Hello Jello" greetings[6...10] = "Peter" // "Hello Peter" greetings[6..11] = "Helen" // "Hello Helen" greetings[6..] = "Jayne" // "Hello Jayne" (end is assumed if unspecified)
As the examples show, several range operators are supported. The first number is always the index of the first element in the segment. The meaning of the number after the range operator varies:
- , - The second number specifies how many elements are in the array segment.
- .. - The second number specifies the index of the element after the last element in the array segment. If unspecified, it is assumed to be the length of the array.
- ... - The second number specifies the index of the last element in the array segment.
Both array segments must have the same element type and length. Array segments can only be used for copying elements from one array to another using assignment or variable initialization. An array segment cannot be passed as an argument in a function call or used in a comparison.
Note: One can also use the ampersand operator to borrow an array reference (slice) to a specific set of elements in an array.
Multi-element fills
Using the same range operators, a single value can be repeatedly filled into a segment's elements:
mut numbers = [0, -1, -2, -3] numbers[1 .. 3] = 2 // numbers == [0, 2, 2, -3]
The type of the fill value must be the same as the type of the array's elements.
Comparing
Two arrays may be compared, so long as they have the same element type.
To be equivalent the arrays must have the same length and every element must be the same. To make element comparison possible, the element's type must support the == method.
imm somebody = "John" if somebody == "Jane": ....
Comparing for order (e.g., <) is also handled by comparing the two arrays element-by-element, stopping the first time two elements are not the same. The number of elements compared is the minimum of the lengths of the two arrays. If the element check shows them as equivalent, the lesser-sized array comes first in order.
Partial pattern match
The match statement or is operator supports additional array comparison capability, particularly for partial pattern matching.