# Arrays

> Array literals, comprehensions, methods, slices, nested arrays, copy-on-write value semantics and reductions over generators in Brine.

Source: https://docs.brinelang.org/language/arrays/

An array `[T]` is a growable list of values of one type. Arrays are **values**: `let b = a` gives you an independent copy (made lazily, so it’s cheap), and an index outside the array stops the program unless the compiler proved it can’t happen.

```brine
fn main():
    var xs = [3, 1, 2]
    xs.push(5)
    xs.sort()
    print(xs, len(xs), xs[0], 5 in xs)
```

Output

```text
[1, 2, 3, 5] 4 1 true
```

## Making arrays

[Section titled “Making arrays”](#making-arrays)

Write the elements in brackets, repeat one value with `* n`, or build an array with a comprehension:

```brine
fn main():
    let primes = [2, 3, 5, 7]
    let zeros = [0.0] * 4
    let squares = [i * i for i in 0..6]
    let even_squares = [i * i for i in 0..10 if i % 2 == 0]
    let words = ["fig", "pear", "plum"]
    let lengths = [len(w) for w in words]
    print(primes, zeros, squares)
    print(even_squares, lengths)
```

Output

```text
[2, 3, 5, 7] [0.0, 0.0, 0.0, 0.0] [0, 1, 4, 9, 16, 25]
[0, 4, 16, 36, 64] [3, 4, 4]
```

* `[x] * n` makes `n` copies of `x`. The value goes on the left: `3 * [0]` is an error. A negative `n` panics.
* A comprehension is `[expr for name in iterable]` with an optional `if filter`. The iterable is a range (`0..n`, `0..=n`, `range(a, b, step)`), an array, a string or a map. Tuples unpack: `[name for name, age in people if age > 30]`.
* One `for` per comprehension: `[x for row in grid for x in row]` is an error (E0110). Use a loop with `extend` instead.
* An empty array needs a type: `var names: [str] = []`. `let xs = []` alone is error [E0308](/reference/errors/#e0308).

`par [...]` builds the array on every core. See [parallelism](/parallelism/overview/).

## Reading elements

[Section titled “Reading elements”](#reading-elements)

```brine
fn main():
    let xs = [10, 20, 30, 40]
    print(xs[0], xs[len(xs) - 1], len(xs))
    print(20 in xs, xs.contains(99), 99 not in xs)
    print(xs.index(30), xs.index(99))
    print(xs == [10, 20, 30, 40], xs != [40, 30, 20, 10])
```

Output

```text
10 40 4
true false true
Some(2) None
true true
```

* `xs[i]` reads element `i`, counting from 0.
* `x in xs` and `xs.contains(x)` are the same test.
* `xs.index(x)` returns `Option[int]`: `Some(i)` for the first match, `None` if there is none. See [error handling](/language/error-handling/) for `Option`.
* `==` and `!=` compare length and every element. Arrays have no `<`: only numbers and strings are ordered.

An index outside the array is a panic:

```brine
fn main():
    let xs = [1, 2, 3]
    let i = len(args()) + 5
    print(xs[i])
```

Output

```text
panic: index 5 is out of bounds for an array of length 3
  --> main.br:4:11
```

The program exits with code 101. When the compiler can prove an index is in range (as in `for i in 0..len(xs): xs[i]`), it removes the check. `brine check --report` lists the checks that remain. See [proofs](/proofs/overview/).

## No negative indexes

[Section titled “No negative indexes”](#no-negative-indexes)

Brine has no negative indexes. `xs[-1]` isn’t the last element; it’s out of bounds, and panics like any other bad index. Write the index out:

```brine
fn main():
    let xs = [1, 2, 3]
    let last = xs[len(xs) - 1]
    let tail = xs[len(xs) - 2:]
    print(last, tail)
```

Output

```text
3 [2, 3]
```

Python’s `xs[-1]` hides an off-by-one when the index is computed: `i - 1` with `i` at 0 silently gives the last element. In Brine it stops with a clear message.

## Changing an array

[Section titled “Changing an array”](#changing-an-array)

Methods that change an array need a `var`:

```brine
fn main():
    var xs = [3, 1, 2]
    xs.push(5)
    let last = xs.pop()
    let first = xs.pop(0)
    print(last, first, xs)
    xs.insert(0, 9)
    xs.extend([7, 8])
    print(xs)
    xs.sort()
    print(xs)
    xs.reverse()
    print(xs)
    xs[0] = 100
    xs[1] += 1
    print(xs)
    xs.clear()
    print(xs, len(xs))
```

Output

```text
5 3 [1, 2]
[9, 1, 2, 7, 8]
[1, 2, 7, 8, 9]
[9, 8, 7, 2, 1]
[100, 9, 7, 2, 1]
[] 0
```

* `pop()` on an empty array panics (`pop() from an empty array`), and so do `pop(i)` and `insert(i, x)` with `i` out of range. `insert(len(xs), x)` appends.
* `sort()` works on arrays of numbers and strings and is stable. Arrays of tuples, structs or `bool` can’t be sorted (E0305).
* There is no `+` for arrays. Use `extend`.

## Slices

[Section titled “Slices”](#slices)

`xs[a:b]` is a new array with the elements from index `a` up to, not including, `b`. Leave out `a` to start at 0 and `b` to go to the end:

```brine
fn main():
    let xs = [10, 20, 30, 40, 50]
    var middle = xs[1:4]
    middle[0] = 0
    print(middle, xs[:2], xs[3:], xs[5:], xs[:])
    print(xs)
```

Output

```text
[0, 30, 40] [10, 20] [40, 50] [] [10, 20, 30, 40, 50]
[10, 20, 30, 40, 50]
```

A slice is a copy: changing it never changes the original. Bounds must satisfy `0 <= a <= b <= len(xs)`; anything else panics, with no clamping as in Python:

```text
panic: slice [1:5] is out of bounds for an array of length 3
```

## Nested arrays

[Section titled “Nested arrays”](#nested-arrays)

An array can hold arrays. Build a grid with a comprehension, so each row is its own value:

```brine
fn main():
    var grid = [[0] * 3 for _ in 0..2]
    grid[1][2] = 7
    let copy = grid
    grid[0][0] = 1
    print(grid)
    print(copy)
    print(len(grid), len(grid[0]), grid[1][2])
```

Output

```text
[[1, 0, 0], [0, 0, 7]]
[[0, 0, 0], [0, 0, 7]]
2 3 7
```

`[[0] * 3] * 2` works too. Because arrays are values, its rows are independent: setting `g[0][0]` changes only the first row, unlike Python lists.

## Arrays are values

[Section titled “Arrays are values”](#arrays-are-values)

Assigning an array, passing it to a function, or storing it in a struct copies it. The copy is lazy (*copy-on-write*): both names share one buffer until one of them changes, and only then is the buffer copied. Copies cost almost nothing, and changing one array never changes another.

```brine
fn add_one(mut xs: [int]) -> [int]:
    for i in 0..len(xs):
        xs[i] += 1
    return xs


fn main():
    var a = [3, 1, 2]
    let b = a
    a.sort()
    let c = add_one(a)
    print(a, b, c)
```

Output

```text
[1, 2, 3] [3, 1, 2] [2, 3, 4]
```

To let a function change the caller’s array, pass it by reference: `fn fill(ref xs: [int], v: int)`, called as `fill(ref data, 0)`. See [functions](/language/functions/).

## Looping over an array

[Section titled “Looping over an array”](#looping-over-an-array)

`for x in xs` walks a snapshot of `xs` taken when the loop starts, so the body may change `xs` safely:

```brine
fn main():
    var xs = [1, 2, 3]
    for x in xs:
        xs.push(x * 10)
    print(xs)
    for i, x in enumerate(["a", "b"]):
        print(i, x)
```

Output

```text
[1, 2, 3, 10, 20, 30]
0 a
1 b
```

The loop runs three times. Changes made in the body show up after the loop. See [control flow](/language/control-flow/) for more loop forms.

## One change per statement

[Section titled “One change per statement”](#one-change-per-statement)

A statement can’t both change a variable and read it somewhere else, or change it twice. Otherwise the result would depend on which part runs first:

```brine
fn main():
    var xs = [1, 2, 3]
    print(xs.pop(), xs)
```

```text
error[E0405]: `xs` is changed and also read in the same expression
 --> main.br:3:11
  |
3 |     print(xs.pop(), xs)
  |           ^^^^^^^^ changed here
  |                     -- read here
  |
  = help: make the change in its own statement first, then use the result
  = run `brine explain E0405` for more about this error
error: could not compile due to 1 error
```

Split it in two: `let last = xs.pop()`, then `print(last, xs)`. The same applies to `xs.push(xs.pop())`. Reading the array in the changing call’s own arguments is fine (`xs.push(len(xs))`), and so is using it on the two sides of `and`, `or` or `if ... else`. See [E0405](/reference/errors/#e0405).

## Reductions

[Section titled “Reductions”](#reductions)

`sum`, `product`, `min`, `max`, `any` and `all` take a generator: a comprehension without the brackets. No array is built.

```brine
fn main():
    let xs = [4, -2, 8, 5]
    print(sum(x for x in xs), product(x for x in xs))
    print(min(x for x in xs), max(x * x for x in xs))
    print(any(x < 0 for x in xs), all(x < 10 for x in xs))
    print(sum(i for i in 0..101 if i % 2 == 0))
    print(sum(float(x) / 2.0 for x in xs))
```

Output

```text
15 -320
-2 64
true true
2550
7.5
```

* Over an empty sequence, `sum` is 0, `product` is 1, `any` is `false` and `all` is `true`. `min` and `max` panic: `min() of an empty sequence`.
* `min(a, b)` and `max(a, b)` with two arguments are the plain built-ins.
* A generator can only be passed to one of these six; `len(x for x in xs)` is error [E0316](/reference/errors/#e0316). Use `[x for x in xs]` to build an array.
* Overflow in a reduction panics like any other overflow.
* `par sum(...)` and the others run on every core and give the same bits on any machine. See [deterministic reductions](/parallelism/deterministic-reductions/).

## Reference

[Section titled “Reference”](#reference)

| Operation                                    | Result        | Notes                                                        |
| -------------------------------------------- | ------------- | ------------------------------------------------------------ |
| `len(xs)`                                    | `int`         |                                                              |
| `xs[i]`                                      | `T`           | panics if out of range; no negative indexes                  |
| `xs[a:b]`, `xs[:b]`, `xs[a:]`                | `[T]`         | a copy; panics if out of range                               |
| `x in xs`, `xs.contains(x)`                  | `bool`        |                                                              |
| `xs.index(x)`                                | `Option[int]` | first match                                                  |
| `xs == ys`                                   | `bool`        | element by element                                           |
| `xs.push(x)`                                 |               | append                                                       |
| `xs.pop()`                                   | `T`           | remove the last; panics if empty                             |
| `xs.pop(i)`                                  | `T`           | remove at `i`, shifting the rest left                        |
| `xs.insert(i, x)`                            |               | put `x` at `i`, shifting the rest right; `0 <= i <= len(xs)` |
| `xs.extend(ys)`                              |               | append every element of `ys`                                 |
| `xs.sort()`                                  |               | numbers and strings; stable                                  |
| `xs.reverse()`                               |               |                                                              |
| `xs.clear()`                                 |               | remove everything                                            |
| `sum`, `product`, `min`, `max`, `any`, `all` |               | over a generator: `sum(x for x in xs)`                       |

## Rules

[Section titled “Rules”](#rules)

* Elements all have one type. An empty array needs an annotation.
* Indexes start at 0 and are never negative. Out-of-range indexes and slices panic.
* `let b = a` copies; changing `b` never changes `a`.
* `for x in xs` loops over a snapshot.
* A statement can’t change an array and read it elsewhere.

Related: [strings](/language/strings/), [maps](/language/maps/), [values and types](/language/values-and-types/), [built-in functions](/language/builtins/).
