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”const LIMIT = 100const GREETING: str = "hello"
fn main(): let x = 10 # immutable var total = 0 # mutable total += x total = total * 2 print(x, total, LIMIT, GREETING)10 20 100 helloA 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.
fn main(): let x = 1 x = 2 print(x)error[E0401]: cannot assign to `x` because it was declared with `let` --> main.br:3:5 |3 | x = 2 | ^ immutable2 | let x = 1 | - declared here | = help: declare it with `var x = ...` to make it mutable = run `brine explain E0401` for more about this errorerror: could not compile due to 1 errorEvery 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 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.
const SIZE = 2 * 50const LIMIT = SIZE + 1const RATE: float = 0.25const NAME = "brine"
fn main(): print(SIZE, LIMIT, RATE, NAME)100 101 0.25 brineA function call or an array isn’t a constant expression
(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).
Type annotations
Section titled “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.
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)0 71 7["ann"] {"ann": 1}fn main(): var names = [] names.push("ann")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 errorerror: could not compile due to 1 errorFunction parameters and return types are always written (see functions); only local variables are inferred.
Tuple unpacking
Section titled “Tuple unpacking”A let or var can take a tuple apart. Patterns nest.
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)3 2 1 2 3 1 10 ann 3To 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.
The _ placeholder
Section titled “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.
It may appear any number of times and can never be read.
fn main(): let (q, _) = (17 / 5, 17 % 5) print(q, _)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 errorerror: could not compile due to 1 errorNo shadowing
Section titled “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.
fn main(): let count = 1 if count > 0: let count = 2 print(count)error[E0207]: `count` is already defined in this function --> main.br:4:13 |4 | let count = 2 | ^^^^^ defined again here2 | 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 errorerror: could not compile due to 1 errorA 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).
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.
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)big5Requiring written types
Section titled “Requiring written types”Some teams want every variable’s type on the page. Put this in brine.toml:
[check]typed = trueor pass --typed to brine check, brine run or brine build. Then every let,
var and for variable needs a written type, and E0337
names each one that’s missing, with the type to add:
fn main(): let n: int = 3 let xs = [1, 2] for i in 0..n: print(i, xs)brine check --typed main.brerror[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 errorerror[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 errorerror: could not compile due to 2 errorsComprehension 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.
letis immutable, all the way down.varis mutable.constis 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 = trueunder[check](or--typed) requires a written type on every variable (E0337).