std/core/array
std/core/src/array.trb
Array, the inline storage primitive: a fixed number of items whose count is part of the type instead of its
storage.
It lives in std/core and not in std/collections because the language itself refers to it, exactly as it refers
to Option behind Value?, to Result behind ? and to Range behind a..b: a list literal against an expected
Array writes its items straight into the inline slots, and the checker counts them against Size. A collection is
a data structure written on top of a storage primitive, and that is what std/collections holds.
Related
List- the growable collection with the same[index]andforvocabulary; everything that can grow is aList, never anArray.
type Array
native type Array<Item, const Size: Int> with Iterate<Item>, Length, MutableIndexed<Int, Item>
A fixed number of items, and the number is part of the type: Array<Float, 16>.
This is the inline storage primitive of the language, not one more collection. An Array is a small value with a
fixed layout: it lives inline (in a binding, in a field, in another Array), has no storage on the heap and no
reference count, and copying it copies its items. Reach for it for vectors and matrices, colors, hashes and foreign
structs; everything that grows is a List instead.
Size is a const parameter: a literal, a named const or another const parameter. There is no arithmetic over one,
so an out-of-bounds index the compiler can work out at the call site is a compile error rather than a panic:
var identity: Array<Float, 4> = Array.filled(0.0)
identity[0] = 1.0
identity[4] = 1.0 // compile error: the index is known to be out of bounds
Examples
An Array comes into being in three ways, and every one of them says its size out loud. A list literal against an
expected Array type writes the items into the slots, and the count has to be Size:
var point: Array<Float, 3> = [1.0, 2.0, 3.0]
point[0] = 0.0
print point[0]
A ... inside such a literal is allowed when what it spreads is itself an Array, because only then is its count
known; the sizes add up to Size:
const half: Array<Int, 2> = [1, 2]
const whole: Array<Int, 3> = [...half, 3]
print whole
Array.filled and Array.generated take the size from the expected type, and Array.from counts at run time and
answers an Option:
const zeros: Array<Int, 4> = Array.filled 0
const squares: Array<Int, 4> = Array.generated { index => index * index }
const parsed: Array<Int, 4>? = Array.from([1, 2, 3, 4])
print "{zeros} {squares} {parsed}"
Pitfalls
Size has to come from somewhere, and a bare Array.filled(0) gives it nothing to come from: the expected type is
the only place it is written. Annotate the binding, the field or the result.
Related
List- the growable collection with the same indexing and iteration.
fn filled
static fn filled(value: Item): Array<Item, Size>
Every item set to value, with the length taken from the expected type.
The value is written straight into the inline slots, one copy per slot, so no list is built and no closure runs.
Examples
const zeros: Array<Int, 8> = Array.filled 0
print zeros
fn generated
static fn generated(produce: (index: Int) => Item): Array<Item, Size>
Every item from its index, with the length taken from the expected type: produce is called once per slot, from
0 to Size - 1, in that order.
Reach for it where the items follow from their position - a ramp, a lookup table, a row of a matrix - and for
Array.filled where they are all the same. The values are written straight into the inline slots, so no list is
built and no iterator runs.
Examples
const squares: Array<Int, 5> = Array.generated { index => index * index }
print squares
fn from
static fn from(items: Iterate<Item>): Array<Item, Size>?
The Array built from items, or None if their count is not Size. The runtime-checked sibling of a literal.
It stops reading as soon as there is one item too many, so an endless items answers None instead of running
forever.
Examples
const parsed: Array<Int, 3>? = Array.from([1, 2, 3])
print parsed
fn length
fn length(): Int
How many items there are: always Size, which the type already says.
fn get
fn get(index: Int): Item?
The item at index, or None if it is out of bounds. array[index] panics instead; see Array.at.
fn at
fn at(index: Int): Item
The item at index. This is array[index].
Panics
When index is out of bounds, with index <index> is out of bounds for a length of <Size>, as a List does.
An index that is written out is checked by the compiler instead.
fn iterate
fn iterate(): Iterator<Item>
A cursor over the items, front to back: see ArrayIterator.
fn set
var fn set(index: Int, value: Item)
Changes the item at index in place. array[index] = value goes through this; see MutableIndexed.set.
Panics
When index is out of bounds, with index <index> is out of bounds for a length of <Size> - the message a List
answers the same mistake with.
fn fill
var fn fill(value: Item)
Sets every item to value, in place.
fn mapped
fn mapped<Output>(transform: Transform<Item, Output>): Array<Output, Size>
Transforms every item into a new Array of the same length, so the result is an Array again.
Examples
const numbers: Array<Int, 3> = [1, 2, 3]
const doubled = numbers.mapped { _ * 2 }
print doubled
extend Array<Item, Size> with Equals
extend<Item: Equals, const Size: Int> Array<Item, Size> with Equals
Equal when the items at the same index are equal. The length is part of the type, so two arrays that compare at all have the same one and there is nothing to test about it.
A collection is Equals when its items are, exactly as a tuple is - and an Array is the storage that is most
like a tuple: inline slots of a fixed width.
fn equals
fn equals(other: Self): Bool
extend Array<Item, Size> with Show
extend<Item: Show, const Size: Int> Array<Item, Size> with Show
The items in brackets, like a List: the size is in the type and would say nothing a reader cannot count.
fn show
fn show(): String
extend Array<Item, Size> with Hash
extend<Item: Hash, const Size: Int> Array<Item, Size> with Hash
Order-dependent, like List.hash: an Array is an ordered sequence.
fn hash
fn hash(): Int