Arrays & Slices

Conjure provides multiple ways to work with collections of values. Fixed-size arrays offer compile-time guarantees and zero overhead, while slices and lists provide dynamic sizing when you need flexibility.

Fixed-Size Arrays

Arrays have a size that’s known at compile time and cannot change, with the size being part of the type. The array size is specified in square brackets before the element type.

var numbers [5]i32 = {10, 20, 30, 40, 50}
var colors [3]string = {"red", "green", "blue"}

Array Initialization

You can initialize arrays with explicit values using braces.

var primes = [5]i32{2, 3, 5, 7, 11}

When the size can be inferred from the number of elements, you can omit it.

var primes = []i32{2, 3, 5, 7, 11}  // size inferred as 5

Initialize all elements to the same value using the array constructor.

var zeros = [100]i32{...0}  // all 100 elements are 0
var ones = [50]f32{...1.0}  // all 50 elements are 1.0

Without explicit initialization, array elements are set to their zero values.

var buffer [1024]u8  // all bytes are 0

Accessing Elements

Array elements are accessed using square brackets with zero-based indexing.

var numbers = [5]i32{10, 20, 30, 40, 50}

var first = numbers[0]   // 10
var third = numbers[2]   // 30
var last = numbers[4]    // 50

Bounds checking is performed in debug builds. In release builds, bounds checking is omitted for performance, so out-of-bounds access is undefined behavior.

Modifying Elements

Change array elements by assigning to an index.

var scores = [3]i32{85, 90, 78}
scores[1] = 95
scores[2] = 82

Multi-Dimensional Arrays

Arrays can be nested to create multi-dimensional structures.

var matrix = [3,3]i32{
    {1, 2, 3},
    {4, 5, 6},
    {7, 8, 9},
}

var center = matrix[1,1]  // 5
matrix[0,2] = 99

You can also write this with repeated brackets.

var matrix [3][3]i32
var value = matrix[1][1]

Both syntaxes are equivalent, though the comma notation is more concise.

Array Length

Get the length of an array using the .len property.

var numbers = [5]i32{1, 2, 3, 4, 5}
var length = numbers.len  // 5

Since array sizes are compile-time constants, .len is also a compile-time constant.

Iterating Over Arrays

Use for-in loops to iterate over array elements.

var names = [3]string{"Alice", "Bob", "Charlie"}

for name in names {
    c.printf("%s\n", name)
}

Access both index and value using the comma syntax.

for i, name in names {
    c.printf("%d: %s\n", i, name)
}

Slices (and Lists)

Slices are views into arrays or other slices. They have a pointer to the underlying data, a length, and a capacity. Slices are written as []T where T is the element type and, unlike languages like Go, cannot be sized larger than the underlying array.

var numbers     = [10]i32{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
var slice []i32 = numbers[2..5]  // slice of elements 2, 3, 4, 5

The reason a slice cannot be sized larger than the underlying array is that they retain a pointer to the original array, allowing modifications to the slice to affect the underlying array. However, slices can be promoted to lists which are dynamically sized arrays that manage their own memory.

To create a list from a slice, you can cast the slice with the target type being List[T] where T is the element type. Lists are part of the standard library’s list module.

var slice []i32
var list = List[i32](slice)  // creates a List[i32] copy of the slice

You can also create lists directly arrays by casting an array to a list.

var arr = [5]i32{1, 2, 3, 4, 5}
var list = List[i32](arr)  // creates a List[i32] copy of the array

Creating Slices

Extract a slice from an array using range syntax.

var arr = [10]i32{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}

var slice1 = arr[2..5]   // elements at indices 2, 3, 4, 5
var slice2 = arr[0...3]  // elements at indices 0, 1, 2, 3
var slice3 = arr[5..]    // from index 5 to end
var slice4 = arr[..5]    // from start to index 5
var slice5 = arr[..]     // entire array as slice

Slices reference the original array’s memory. Modifying a slice modifies the underlying array.

var numbers = [5]i32{1, 2, 3, 4, 5}
var slice = numbers[1..3]

slice[0] = 99
c.printf("%d\n", numbers[1])  // prints 99

Slice Properties

Slices have both length and capacity. Length is the number of elements in the slice and capacity reflects the maximum size of the underlying array from the starting index of the slice.

var numbers = [10]i32{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
var slice = numbers[2..5]

var length = slice.len    // 4 (indices 2, 3, 4, 5)
var capacity = slice.cap  // 8 (can grow to index 9)

String Arrays

Arrays and lists of strings work the same as any other type.

var names = [3]string{"Alice", "Bob", "Charlie"}

Memory Layout

Fixed arrays are allocated inline, either on the stack for local variables or embedded in structs.

struct Point {
    coords [3]f32  // embedded in the struct
}

Slices are lightweight views containing a pointer, length, and capacity. They don’t own their data.

Performance Considerations

Fixed arrays have zero overhead and optimal cache locality. Use them when the size is known at compile time.

Slices add minimal overhead for the length and capacity tracking. They’re ideal for passing array subsets to functions.

Passing to Functions

Arrays are passed by value, meaning they’re copied. For large arrays, use slices or pointers to avoid expensive copies.

// Copies the entire array
func processArray(arr [1000]i32) {
    // ...
}

// Passes a slice (just pointer + length + capacity)
func processSlice(slice []i32) {
    // ...
}

Slices and lists are already lightweight references, so passing them is cheap.

Array Literals in Expressions

Array literals can be used anywhere an array value is expected.

var max = findMax([5]i32{3, 7, 2, 9, 1})

func findMax(arr [5]i32) i32 {
    var max = arr[0]
    for value in arr {
        if value > max {
            max = value
        }
    }
    return max
}

Nested Collections

You can nest arrays, slices, and lists in any combination.

var grid = [10][10]i32{}
var jaggedArray = List[[]i32]{}
var matrix = List[List[f32]]{}

This flexibility lets you choose the right collection type for each level of nesting.