Arrays
Arrays have a central role in programming. C3 offers built-in arrays, slices and vectors. The standard library enhances this further with dynamically sized arrays and other collections.
Fixed Size 1D Arrays
Section titled “Fixed Size 1D Arrays”These are declared as <type>[<size>], e.g. int[4]. Fixed arrays are treated as values and will be copied if given as parameter. Unlike C, the number is part of its type. Taking a pointer to a fixed array will create a pointer to a fixed array, e.g. int[4]*.
Unlike C, fixed arrays do not decay into pointers. Instead, an int[4]* may be implicitly converted into an int*.
// Cint foo(int *a) { ... }
int x[3] = { 1, 2, 3 };foo(x);
// C3fn int foo(int* a) { ... }
int[3] x = { 1, 2, 3 };foo(&x);When you want to initialize a fixed array without specifying the size, use the [*] array syntax:
int[3] a = { 1, 2, 3 };int[*] b = { 4, 5, 6 }; // Type inferred to be int[3]You can get the length of an array using the .len property:
int len1 = int[4].len; // 4int[3] a = { 1, 2, 3 };int len2 = a.len; // 3int[*] b = { 1, 2 };int len3 = b.len; // 2The final type is the slice <type>[] e.g. int[]. A slice is a view into either a fixed or variable array. Internally it is represented as a struct containing a pointer and a size. Both fixed and variable arrays may be converted into slices, and slices may be implicitly converted to pointers.
fn void test(){ int[4] arr = { 1, 2, 3, 4 }; int[4]* ptr = &arr;
// Assignments to slices int[] slice1 = &arr; // Implicit conversion int[] slice2 = ptr; // Implicit conversion
// Assignments from slices int[] slice3 = slice1; // Assign slices from other slices int* int_ptr = slice1; // Assign from slice int[4]* arr_ptr = (int[4]*)slice1; // Cast from slice}Slicing Arrays
Section titled “Slicing Arrays”It’s possible to use the range syntax to create slices from pointers, arrays, and other slices.
This is written arr[<start-index> .. <end-index>], where end-index is inclusive.
fn void test(){ int[5] a = { 1, 20, 50, 100, 200 };
int[] b = a[0 .. 4]; // The whole array as a slice. int[] c = a[2 .. 3]; // { 50, 100 }}You can also use arr[<start-index> : <slice-length>]
fn void test(){ int[5] a = { 1, 20, 50, 100, 200 };
int[] b2 = a[0 : 5]; // { 1, 20, 50, 100, 200 } start-index 0, slice-length 5 int[] c2 = a[2 : 2]; // { 50, 100 } start-index 2, slice-length 2}It’s possible to omit the first and last indices of a range:
arr[..<end-index>]Omitting the start index will default it to 0arr[<start-index>..]Omitting the end index will assign it toarr.len-1(this is not allowed on pointers)
Equivalently with index offset arr[:<slice-length>] you can omit the start-index
The following are all equivalent and slice the whole array
fn void test(){ int[5] a = { 1, 20, 50, 100, 200 };
int[] b = a[0 .. 4]; int[] c = a[..4]; int[] d = a[0..]; int[] e = a[..];
int[] f = a[0 : 5]; int[] g = a[:5];}You can also slice in reverse from the end with ^i where the index is len-i for example:
^1meanslen-1^2meanslen-2^3meanslen-3
Again, this is not allowed for pointers since the length is unknown.
fn void test(){ int[5] a = { 1, 20, 50, 100, 200 };
int[] b1 = a[1 .. ^1]; // { 20, 50, 100, 200 } a[1 .. (a.len-1)] int[] b2 = a[1 .. ^2]; // { 20, 50, 100 } a[1 .. (a.len-2)] int[] b3 = a[1 .. ^3]; // { 20, 50 } a[1 .. (a.len-3)]
int[] c1 = a[^1..]; // { 200 } a[(a.len-1)..] int[] c2 = a[^2..]; // { 100, 200 } a[(a.len-2)..] int[] c3 = a[^3..]; // { 50, 100, 200 } a[(a.len-3)..]
int[] d = a[^3 : 2]; // { 50, 100 } a[(a.len-3) : 2]
// Slicing a whole array, the inclusive index of : gives the difference int[] e = a[0 .. ^1]; // a[0 .. a.len-1] int[] f = a[0 : ^0]; // a[0 : a.len]
}One may also assign to slices:
int[3] a = { 1, 20, 50 };a[1..2] = 0; // a = { 1, 0, 0 }Or copy slices to slices:
int[3] a = { 1, 20, 50 };int[3] b = { 2, 4, 5 };a[1..2] = b[0..1]; // a = { 1, 2, 4 }Copying between two overlapping ranges, e.g. a[1..2] = a[0..1] is unspecified behaviour.
Conversion List
Section titled “Conversion List”int[4] | int[] | int[4]* | int* | |
|---|---|---|---|---|
int[4] | copy | - | - | - |
int[] | - | assign | assign | - |
int[4]* | - | cast | assign | cast |
int* | - | assign | assign | assign |
Note that all casts above are inherently unsafe and will only work if the type cast is indeed compatible.
For example:
int[4] a;int[4]* b = &a;int* c = b;
// Safe cast:int[4]* d = (int[4]*)c;int e = 12;int* f = &e;
// Incorrect, but not checkedint[4]* g = (int[4]*)f;
// Also incorrect but not checked.int[] h = f[0..2];Internals
Section titled “Internals”Internally the layout of a slice is guaranteed to be struct { <type>* ptr; usz len; }.
There is a built-in struct std::core::runtime::SliceRaw which
has the exact data layout of the fat array pointers. It is defined to be
struct SliceRaw{ void* ptr; usz len;}Iteration Over Arrays
Section titled “Iteration Over Arrays”foreach element by copy
Section titled “foreach element by copy”You may iterate over slices, arrays and vectors using foreach (Type x : array).
Using compile-time type inference this can be abbreviated
to foreach (x : array) for example:
fn void test(){ int[4] arr = { 1, 2, 3, 5 }; foreach (item : arr) { io::printfn("item: %s", item); }
// Or equivalently, writing the type: foreach (int x : arr) { /* ... */ }}foreach element by reference
Section titled “foreach element by reference”Using & it is possible to get an element by reference rather than by copy.
Providing two variables to foreach, the first is assumed to be the index and the second the value:
fn void test(){ int[4] arr = { }; foreach (idx, &item : arr) { *item = 7 + idx; // Mutates the array element }
// Or equivalently, writing the types foreach (int idx, int* &item : arr) { *item = 7 + idx; // Mutates the array element }}foreach_r reverse iterating
Section titled “foreach_r reverse iterating”With foreach_r arrays or slices can be iterated over in reverse order
fn void test(){ float[4] arr = { 1.0, 2.0 }; foreach_r (idx, item : arr) { // Prints 2.0, 1.0 io::printfn("item: %s", item); }
// Or equivalently, writing the types foreach_r (int idx, float item : arr) { // Prints 2.0, 1.0 io::printfn("item: %s", item); }}Iteration Over Array-Like types
Section titled “Iteration Over Array-Like types”It is possible to enable foreach on any custom type
by implementing .len and [] methods and annotating them using the @operator attribute:
struct DynamicArray{ usz count; usz capacity; int* elements;}
macro int DynamicArray.get(DynamicArray* arr, usz element) @operator([]){ return arr.elements[element];}
macro usz DynamicArray.count(DynamicArray* arr) @operator(len){ return arr.count;}
fn void DynamicArray.push(DynamicArray* arr, int value){ arr.ensure_capacity(arr.count + 1); // Function not shown in example. arr.elements[arr.count++] = value;}
fn void test(){ DynamicArray v; v.push(3); v.push(7);
// Will print 3 and 7 foreach (int i : v) { io::printfn("%d", i); }}For more information, see operator overloading
Dynamic Arrays and Lists
Section titled “Dynamic Arrays and Lists”The standard library offers dynamic arrays and other collections in the std::collections module.
alias ListStr = List {String};
fn void test(){ ListStr list_str;
// Initialize the list on the heap. list_str.init(mem);
list_str.push("Hello"); // Add the string "Hello" list_str.push("World");
foreach (str : list_str) { io::printn(str); // Prints "Hello", then "World" } String str = list_str[1]; // str == "World" list_str.free(); // Free all memory associated with list.}Fixed Size Multi-Dimensional Arrays
Section titled “Fixed Size Multi-Dimensional Arrays”To declare two dimensional fixed arrays as <type>[<x-size>, <y-size>] arr, like int[4][2] arr. Below you can see how this compares to C:
// C// Uses: name[<rows>][<columns>]int array_in_c[4][2] = { {1, 2}, {3, 4}, {5, 6}, {7, 8},};
// C3// Uses: <type>[<x-size>][<y-size>]// C3 declares the dimensions, inner-most to outer-mostint[4][2] array = { {1, 2, 3, 4}, {5, 6, 7, 8},};
// To match C we must invert the order of the dimensionsint[2][4] array = { {1, 2}, {3, 4}, {5, 6}, {7, 8},};
// C3 also supports Irregular arrays, for example:int[][4] array = { { 1 }, { 2, 3 }, { 4, 5, 6 }, { 7, 8, 9, 10 },};