# Bindings

> Declaring names with let, var and const, type annotations, tuple unpacking, the no-shadowing rule, the _ placeholder, and requiring written types with typed = true.

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

`let` declares a name you can’t change, `var` one you can, and `const` a compile-time constant at the top level. Types are inferred unless you write them. A name can’t be declared again while it’s still in scope (no shadowing).

## let and var

[Section titled “let and var”](#let-and-var)

```brine
const LIMIT = 100
const GREETING: str = "hello"


fn main():
    let x = 10              # immutable
    var total = 0           # mutable
    total += x
    total = total * 2
    print(x, total, LIMIT, GREETING)
```

Output

```text
10 20 100 hello
```

A `let` can never be assigned to or changed in place, and neither can anything inside it: no `xs.push(...)` on a `let` array, no `p.x = 1` on a `let` struct, no `ref` method calls on it.

```brine
fn main():
    let x = 1
    x = 2
    print(x)
```

```text
error[E0401]: cannot assign to `x` because it was declared with `let`
 --> main.br:3:5
  |
3 |     x = 2
  |     ^ immutable
2 |     let x = 1
  |         - declared here
  |
  = help: declare it with `var x = ...` to make it mutable
  = run `brine explain E0401` for more about this error
error: could not compile due to 1 error
```

Every variable starts with a value: `var x: int` on its own is a syntax error. The compound assignments are `+=`, `-=`, `*=`, `/=` and `%=`. There are none for the bitwise operators: write `x = x & mask`.

## const

[Section titled “const”](#const)

`const` declares a constant at the top level of a module. Its value is computed at compile time from literals, other constants, arithmetic, comparisons and logic.

```brine
const SIZE = 2 * 50
const LIMIT = SIZE + 1
const RATE: float = 0.25
const NAME = "brine"


fn main():
    print(SIZE, LIMIT, RATE, NAME)
```

Output

```text
100 101 0.25 brine
```

A function call or an array isn’t a constant expression ([E0317](/reference/errors/#e0317)). The exception is `embed("file.txt")`, which reads a file into a `str` at compile time and works in a `const` too (see [built-ins](/language/builtins/)).

## Type annotations

[Section titled “Type annotations”](#type-annotations)

Write a type after the name, with a colon. Annotations are optional, as in Python, but you need one when the compiler can’t infer the type, as for an empty array or map.

```brine
fn main():
    let n: i32 = 7                  # annotate whenever you like
    var names: [str] = []           # ...and when the type can't be inferred
    var counts: {str: int} = {}
    names.push("ann")
    counts["ann"] = 1
    for i: int in 0..2:             # loop variables too
        print(i, n)
    print(names, counts)
```

Output

```text
0 7
1 7
["ann"] {"ann": 1}
```

```brine
fn main():
    var names = []
    names.push("ann")
```

```text
error[E0308]: cannot infer the element type of an empty array
 --> main.br:2:17
  |
2 |     var names = []
  |                 ^^ type unknown
  |
  = help: annotate the variable, e.g. `var xs: [int] = []`
  = run `brine explain E0308` for more about this error
error: could not compile due to 1 error
```

Function parameters and return types are always written (see [functions](/language/functions/)); only local variables are inferred.

## Tuple unpacking

[Section titled “Tuple unpacking”](#tuple-unpacking)

A `let` or `var` can take a tuple apart. Patterns nest.

```brine
fn divmod(a: int, b: int) -> (int, int):
    return (a / b, a % b)


fn main():
    let (q, r) = divmod(17, 5)
    let (a, (b, c)) = (1, (2, 3))
    var (lo, hi) = (0, 10)
    lo += 1
    let (first, _) = ("ann", 31)
    let (_, _, last) = (1, 2, 3)
    print(q, r, a, b, c, lo, hi, first, last)
```

Output

```text
3 2 1 2 3 1 10 ann 3
```

To annotate an unpacking, write the tuple type after the pattern: `let (q, r): (int, int) = (17 / 5, 17 % 5)`. `for` loops unpack the same way (`for name, age in pairs:`); see [control flow](/language/control-flow/#tuple-unpacking-in-for).

## The `_` placeholder

[Section titled “The \_ placeholder”](#the-_-placeholder)

`_` stands for a value you don’t need: in unpacking (`let (q, _) = ...`), as a loop variable (`for _ in 0..n:`), and as a [`match` pattern](/language/enums-and-match/#patterns). It may appear any number of times and can never be read.

```brine
fn main():
    let (q, _) = (17 / 5, 17 % 5)
    print(q, _)
```

```text
error[E0201]: cannot find value `_`
 --> main.br:3:14
  |
3 |     print(q, _)
  |              ^ not found in this scope
  |
  = help: `_` ignores a value; give the value a name to use it
  = run `brine explain E0201` for more about this error
error: could not compile due to 1 error
```

## No shadowing

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

You can’t declare a name that is already in scope in the same function, not even in an inner block. Reading code, a name always means the one variable.

```brine
fn main():
    let count = 1
    if count > 0:
        let count = 2
        print(count)
```

```text
error[E0207]: `count` is already defined in this function
 --> main.br:4:13
  |
4 |         let count = 2
  |             ^^^^^ defined again here
2 |     let count = 1
  |         ----- first defined here
  |
  = help: brine doesn't allow shadowing; pick a new name, or declare it with `var` and assign to it
  = run `brine explain E0207` for more about this error
error: could not compile due to 1 error
```

A parameter counts too: `fn f(n: int)` can’t declare `let n`. Use `mut n: int` if you want to change a local copy (see [functions](/language/functions/#mut-parameters)).

Blocks that don’t overlap may reuse a name: two `for i` loops one after the other, or the same name in the `if` and the `else` branch.

```brine
fn main():
    var total = 0
    for _ in 0..3:
        total += 1
    for i in 0..2:
        total += i
    for i in 0..2:              # a new loop: `i` can be used again
        total += i
    if total > 3:
        let note = "big"
        print(note)
    else:
        let note = "small"
        print(note)
    print(total)
```

Output

```text
big
5
```

## Requiring written types

[Section titled “Requiring written types”](#requiring-written-types)

Some teams want every variable’s type on the page. Put this in `brine.toml`:

brine.toml

```toml
[check]
typed = true
```

or pass `--typed` to `brine check`, `brine run` or `brine build`. Then every `let`, `var` and `for` variable needs a written type, and [E0337](/reference/errors/#e0337) names each one that’s missing, with the type to add:

```brine
fn main():
    let n: int = 3
    let xs = [1, 2]
    for i in 0..n:
        print(i, xs)
```

```sh
brine check --typed main.br
```

```text
error[E0337]: `xs` has no written type
 --> main.br:3:9
  |
3 |     let xs = [1, 2]
  |         ^^ add `: [int]`
  |
  = note: this project asks for a type on every variable (`typed = true` in brine.toml, or `--typed`)
  = run `brine explain E0337` for more about this error
error[E0337]: `i` has no written type
 --> main.br:4:9
  |
4 |     for i in 0..n:
  |         ^ add `: int`
  |
  = note: this project asks for a type on every variable (`typed = true` in brine.toml, or `--typed`)
  = run `brine explain E0337` for more about this error
error: could not compile due to 2 errors
```

Comprehension variables (`[x * x for x in xs]`), `match` bindings and `_` don’t need a type even then: their types come from the code right next to them.

## Rules

[Section titled “Rules”](#rules)

* `let` is immutable, all the way down. `var` is mutable.
* `const` is top-level and computed at compile time.
* Types are inferred for locals; annotate with `name: Type`. Empty `[]` and `{}` need one.
* `let (a, (b, c)) = ...` unpacks tuples, nested.
* `_` ignores a value and can’t be read.
* No shadowing: a name can’t be declared again while it’s in scope, including parameters.
* `typed = true` under `[check]` (or `--typed`) requires a written type on every variable (E0337).
