What if we want the ability to manipulate several values of the same type, all stored together sequentially in memory? Cone offers these safe, built-in types to support this capability:
- Static Array - a fixed-size collection of elements whose size is known at compile-time.
- Dynamic Array - a resizable collection of elements allocated on the heap at runtime.
- Slice - a borrowed reference providing access to a subset of elements stored in some array.
Other indexed collection types (e.g., vectors and maps) use these built-in types (or raw pointers) as a foundation.
Declaration and Creation
As a quick glance, these examples show the type signatures for the built-in array types listed above:
imm staticarray  f32 // Static array imm arrref &own  f32 // Dynamic array (allocated array reference) imm slice & f32 // Slice (borrowed array reference)
Note that dynamic arrays and slices are in fact a special kind of reference: an array reference. Dynamic arrays are array references managed by an allocator. Slices are borrowed array references.
A static array is a fixed-size collection of elements whose size is known at compile-time. It can exist as a global variable or allocated on the stack like any other local variable. The type signature for a static array specifies the number of elements as an unsigned integer literal inside square brackets, followed by the type of every element:
mut text  u32
A static array is initialized using an array constructor, a comma-separated enumeration of same-typed values enclosed within square brackets:
mut list = [1f, 4f, 5f] //  f32
As one would expect, a global variable declaration requires the array constructor to contain only literal values. All other uses of the array constructor allow the array values to be expressions.
A static array may also be initialized using a string literal:
imm hellotext = "Hello!" //  u8
A dynamic array is a dynamically-sized collection of elements allocated on the heap at runtime. A dynamic array is represented by an array reference, which is effectively a tuple containing:
- a pointer to the first element of the array
- an unsigned integer specifying how many elements are in the array
Allocation of a dynamic array looks similar to simple allocations, returning an array reference as a result. There are several different ways to allocate a dynamic array:
- Copy another array. Specify the array to copy from as part of the allocation:
imm dynarr1 = &own [1,2,3]
- Replicate an element.
Specify a count in square brackets followed by the initial value for all elements:
imm dynarr2 = &gc  0. // newarr's type: &gc  f32
Both the count and the value may be expressions.
- Use an element value generator.
Specify a count in square brackets followed by a function (or closure) that calculates each element's initial value:
imm dynarr3 = &rc  squarefn
The function will be called for each element, passing it the index number. The function's return value becomes the element's initial value and type.
Resizing is only possible for an &own-based dynamic array (array reference). This restriction ensures memory safety, protecting against invalidating other references as a result of altering the size or position of a dynamic reference.
Resizing uses the resize command with similar format as above:
- Append another array.
This increases the size by the number of elements being added,
then copies the specified array to the end:
resize dynarr1 [4,5]
- Shrink or grow via element replication.
Specify a new count in square brackets followed by the initial value for all added elements:
resize dynarr2  0. // Grow it. Added elements initialized to zero resize dynarr2  // shrink it. No initial value needed
Both the count and the value may be expressions.
- Use an element value generator.
Like above, specifying a function (or closure) that calculates each added element's initial value:
resize dynarr2  squarefn
The function will be called for each added element, passing it the index number.
A slice is a borrowed array reference providing direct access to a subset of elements stored in some array. It is created by applying an index range to an existing array or slice via the ampersand (&) operator. The range specifies two unsigned integers within square brackets, separated using the '..', '...' or 'by' operator. '..' excludes the last specified element, whereas '...' includes it. The second number on 'by' specifies how many elements are in the slice.
imm x = &"Abcd"[1..3] // a slice pointing to "bc" imm y = &"Abcd"[1...3] // a slice pointing to "bcd" imm z = &"Abcd"[2 by 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)
A slice may also be created from a normal reference or a pointer:
imm slice1 = &ref[..] // Slice count from reference is always 1 imm slice2 = &ptr[0...5]
And, finally, If a static array is passed as a call argument where a slice is expected, it is automatically converted into a slice.
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 restructured while the slice is active. A slice may not be created from an array that has the shared, mutable permission 'mut', as this could lead to potential memory safety problems.
Arrays may be indexed, copied, compared, iterated over, resized, and queried for their properties. These operations work the same way on static arrays, dynamic array, and slices, except where otherwise noted.
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 = c_array // the 4th element is now the same as the third
Array indexing is automatically bounds-checked by its known size. This technique works with pointers as well, but never performs any bounds-check. Using a pointer this way can improve performance where we know the index is safe.
A borrowed reference to a single element can be obtained using the ampersand (&) operator:
imm borref = &sliceref // borrow reference to 5th element
Multiple elements of an array may be extracted and 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 a slice's elements:
arrref[1 .. 3] = 2
The each block provides an easy way to iterate over a static array, dynamic array, or slice:
mut sum = 0 each x in intslice sum += x
Iteration can return a borrowed reference instead of the value:
each &mut x in intslice ++x
Iteration can also show the index number for each value:
each i, x in intslice newslice[i] = x
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 allocated 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 (see below).
Use iteration to compare the contents in two arrays.
The component parts of an array or array reference can be retrieved separately:
imm ptr = array.ptr // The pointer to the first element imm len = array.len // The number of elements imm count = array.count // The number of elements imm size = array.size // The memory size used
Although .count and .len returns the same value for these types, this is not necessarily true for other types, where .len represents the capacity and .count represents how many elements contain a stored value.
Structs as indexed collections
More complex collections may be created using the built-in array types as a foundation. This is typically accomplish by defining a struct with a dynamic array as one of the fields. This custom collection can handle all of the operations described earlier because these capabilities are invoked as methods on the struct object. The struct's methods can then forward these requests down to the dynamic array as appropriate.
These are the implicit names for these methods:
-  - single-element indexing (get and set) if there is one argument, and multi-element indexing if there are two.
- & - get borrowed reference to a single element