Note: None of this is implemented.

Array references offer the ability to create and work with arrays whose size is determined, and is changeable, at runtime. As much as possible, working with dynamic arrays is very similar to static, fixed-size arrays. However, there are some important differences.

Creation

Let's allocate a new dynamic array:

imm nameref = +[]rc "Jan"

This should look familar. We use the +[] array allocator operator followed by the region (rc) used to allocate a new dynamic array. Its contents hold a copy of the 3-byte array given by the string literal above. The allocation returns an owning array reference that points to this value. Unlike regular references that point to a single value of a type, array references point to multiple values of the same type.

The type signature of an array reference reflects this difference. The type signature of nameref is &[]rc u8. Like a static array, array references use square brackets to indicate the reference points to a dynamic array of bytes. No number is enclosed in the brackets since we won't know the array's size until runtime.

Creation Formats

There are several ways to specify the initial values of a dynamic array:

Resizing and Appending

Resizing a dynamic array is only possible when the owning array reference has the uni permission. This restriction ensures memory safety, as it protects against invalidating other references that don't know the new size of the array. A resized array may end up moving to a new memory location.

Resizing uses the <- append operator, along with providing values for the new elements of the larger array:

A reference array may be shrunk by setting a new size that is lower or the same as the current size:

dynarr2.len = 2

Slices (Borrowed References)

A slice is simply a borrowed array reference. It may be borrowed from any array or array reference in the usual way:

imm nbrSlice = &[1, 2, 3]
imm textSlice = &"Padma"
imm dynSlice = &*dynarr2   // from a owning array reference

These slices effectively point to all elements of the source's array.

If a static array is passed as a call argument where a slice is expected, it is automatically converted into a slice:

print("abc")    // equivalent to print(&"abc")

Safety Note: Because a slice is a borrowed reference, its lifetime is limited, ensuring it can never outlive the source array it provides a view into. Depending on permissions, it may temporarily lock out access to the variable it borrows from, thereby ensuring the original array cannot be moved or resized while the slice is active.

Subslices

It is also possible to create a new slice that points to some subset of the elements in some array or array reference. This is accomplished by indexing using a range operator between two unsigned integers:

For example:

imm x = &"Abcd"[1..3]    // a slice pointing to "bc"
imm y = &"Abcd"[1...3]   // a slice pointing to "bcd"
imm z = &"Abcd"[2, 1]  // a slice pointing to "c"

If either index integer is unspecified, 0 and end are assumed.

imm r = &y[1..]        // a slice pointing to "cd" (end assumed)
imm s = &y[..]         // a slice pointing to "bcd" (0 and end assumed)

An index integer may be specified relative to the last element:

imm r = &y[$-2..]      // a slice pointing to "cd"

A slice may also be created from a regular reference or a pointer:

imm slice1 = &ref[..]      // Slice count from reference is always 1
imm slice2 = &ptr[0...5]

Operations

Array references (owning or borrowed) may be indexed, copied, compared, iterated over, resized, and queried for their properties. Many of these operations work the same way they do with static arrays.

Access an Element

A single element within an array may be accessed using an unsigned integer value enclosed in square brackets. This supports both getting and setting the values at a specific index:

c_array[3] = c_array[2]    // the 4th element is now the same as the third

Indexing is automatically bounds-checked by its known size.

Borrowed Reference to an Element

One can create a borrowed reference to any element of a dynamic array in the same way as for an static array:

imm nbrRef = &nbrSlice[1]     // points to 2
imm charRef = &textSlice[2]   // points to 'r'

Borrowing a reference to a single element is not a slice. It is just a regular borrowed reference to some value.

Copy or Fill Elements

Multiple elements of an array may be copied into another array using a range in square brackets. Both content segments must have the same type and size.

arrref[1 by 2] = sliceref[0 by 2]
greetings[n by 5] = "Kevin"

Similarly, a single value can be repeatedly filled into an array reference's elements:

arrref[1 .. 3] = 2

Iteration

The each loop may iterate over an array reference's elements:

mut sum = 0
each x in intslice:
    sum += x

Iteration can also obtain the index number for each value:

each i, x in intslice:
    newslice[i] = x

Comparison

Two array references may be compared for equality, so long as they have the same element type, even if one is a borrowed reference and the other an owning reference. This comparison checks for equality of both the starting pointer and the element count.

if ownref == borref:
   ....

Comparing for order (e.g., <) is not possible directly. One can of course compare the component parts of an array reference separately.

Property Queries

An array reference is a "fat" reference, as it holds two pieces of information:

The component parts of an array reference can be obtained:

imm ptr = array.ptr     // The pointer to the first element
imm len = array.len     // The number of elements

Collection Types

One can create many different kinds of collection types using a single-owner array reference as a private field within some struct. The struct can define operator methods (such as indexing), so that the resulting type behaves very much like an array reference. These methods can forward these requests, as appropriate, on to the array reference field.

_