Skip to content

Arrays and Slices

This page defines DQ array types and their ownership rules. See the Types guide for introductory examples.

Array Forms

Form Ownership Length Resizable
[N]T owns inline element storage compile-time N no
[?]T owns inferred inline storage inferred from initializer no
[]T borrows contiguous storage runtime no
[*]T owns managed heap storage runtime yes

All forms contain elements of one element type. .length is the number of elements, never the number of bytes. Dynamic arrays additionally expose .capacity.

Fixed Arrays

[N]T is a value type. Its size is part of its type and its storage follows the containing variable or aggregate.

var zeros : [4]int = {}
var values : [3]int = [10, 20, 30]
var inferred : [?]int = [1, 2, 3]

[?]T is valid only where an initializer supplies an array literal from which the length can be determined. Different fixed lengths are different types.

Assignment between compatible fixed arrays copies the elements. Fixed arrays cannot change length or capacity.

Dynamic Arrays

[*]T is a managed dynamic array. Assignment shares its manager/storage; a structural mutation may allocate, detach, or update the shared manager according to the runtime operation. Use Clone() when an independent copy is required.

var values : [*]int = [1, 2, 3]
values.Append(4)
var independent : [*]int = values.Clone()

The dynamic operations include Append, Prepend, Insert, Delete, Pop, PopFirst, SetLength, SetCapacity, Reserve, Compact, Clear, and Clone. Operations preserve element order except where their name explicitly removes or inserts elements.

Increasing length initializes new elements. Decreasing length removes trailing elements. Capacity is never less than length. Reserve only grows capacity; Compact reduces it to the current length; Clear(true) may release storage.

Slices

[]T is a non-owning pointer-and-length view. Creating a slice does not copy or retain the owner. Mutating an element through a mutable slice mutates the viewed storage.

var data : [*]int = [10, 20, 30, 40]
var middle : []int = data[1:3]
middle[0] = 99

A slice remains valid only while its owner remains alive and the viewed storage does not move or disappear. Structural changes to a dynamic array can invalidate all slices and element pointers into it, even when the change appears to affect a different part of the array. A slice cannot resize its owner.

Array values may convert to a full compatible slice in view contexts, including function parameters. A slice may not be made from temporary storage whose lifetime would end before the slice.

Indexing

array[index] selects one element. The index must be in the half-open range [0, length). An invalid access raises a runtime range error; ordinary indexing does not clamp.

$end denotes the end position in index/slice syntax. Negative and end-relative indices are normalized according to the operation. Operations that require an existing element reject the one-past-end position; insertion may accept it to append.

Slicing

array[start:end] uses a half-open interval: it contains start and excludes end. array[start::end] is inclusive of both endpoints. Either bound may be omitted.

data[:]        // whole array
data[1:]       // from element 1
data[:3]       // elements 0, 1, 2
data[1:$end]   // element 1 through the last element
data[1::3]     // elements 1, 2, 3

For array storage, a slice expression produces a view. Slice bounds are forgiving: each bound is clamped to [0, length], and a normalized end before the start produces an empty slice. The inclusive form is normalized as a half-open end one position after the requested inclusive endpoint, without integer overflow.

Aliasing and Overlap

Operations accepting elements or a source slice must behave correctly when the source aliases the destination, including after reallocation. The runtime preserves the logical source values before overwriting overlapping storage.

Function Parameters

Use []T for a borrowed mutable view. Apply the appropriate reference parameter mode when the parameter itself must be rebound or when read/write restrictions are required. Pass [*]T by value to share its managed value; use a reference mode when a function must replace the caller's dynamic array variable.

Array equality is not a general implicit operation. Compare lengths and elements explicitly when sequence equality is needed.