From lowest to highest:
| Precedence | Operators | Description |
|---|---|---|
| 1 | |> |
Pipeline |
| 2 | || |
Logical or |
| 3 | && |
Logical and |
| 4 | == != < > <= >= |
Comparison |
| 5 | .. ..= |
Range |
| 6 | + - | ^ |
Add/subtract, bitwise or/xor |
| 7 | * / % << >> & &^ |
Multiply/divide, shifts, bitwise and/and-not |
| 8 | as |
Type cast |
| 9 | - ! ^ & |
Prefix (negation, not, bitwise not, reference) |
| 10 | . () [] ? .* |
Postfix (access, call, index, propagate, deref) |
All binary operators are left-associative.
a + b * c // a + (b * c)
a && b || c // (a && b) || c
a + b |> f // f(a + b)
0..1 + 2 // 0..(1 + 2)+, -, *, /, % require both operands to be the same numeric type. An untyped numeric literal adapts to the other operand.
Unary - negates a number. Disallowed for unsigned types:
[error] Cannot negate unsigned type
╭─[example.lis:1:16]
1 │ let x: uint8 = -1
· ─┬
· ╰── cannot negate `uint8`
╰────
help: Unsigned types cannot represent negative values
+ also concatenates strings:
let greeting = "hello" + ", " + "world"== and != compare any two values of the same type.
<, >, <=, >= compare numeric types and strings.
All comparison operators return bool.
&& and || short-circuit: the right operand is not evaluated if the left determines the result. ! negates. All require bool operands.
if is_valid && count > 0 {
process()
}&, |, ^, and &^ operate on integer values. Shifts (<<, >>) require an integer left operand and any integer right operand; the result has the left operand's type.
let mask = 0b1111
let value = 0b1010
let masked = value & mask
let toggled = value ^ mask
let shifted = value << 2
let inverted = ^valueThe pipeline operator |> passes the left side as the first argument to the function on the right.
x |> f // f(x)
x |> f(y) // f(x, y)
x |> f(y, z) // f(x, y, z)Chains read top to bottom:
let result = items
|> filter(is_valid)
|> map(transform)
|> sum()
// equivalent to: sum(map(filter(items, is_valid), transform))The right side must be a function call. Lambdas are not allowed as pipeline targets.
Range operators .. and ..= create range values.
| Syntax | Type | Description |
|---|---|---|
start..end |
Range<T> |
Exclusive upper bound |
start..=end |
RangeInclusive<T> |
Inclusive upper bound |
start.. |
RangeFrom<T> |
No upper bound |
..end |
RangeTo<T> |
Exclusive, no lower bound |
..=end |
RangeToInclusive<T> |
Inclusive, no lower bound |
Ranges are used in for loops and slice indexing:
for i in 0..5 {
fmt.Println(i) // 0, 1, 2, 3, 4
}
for i in 0..=5 {
fmt.Println(i) // 0, 1, 2, 3, 4, 5
}
let slice = items[1..4] // elements at indices 1, 2, 3Slice sub-slicing is safe by default. The resulting sub-slice has its capacity capped to its length, so append on a sub-slice always allocates a new backing array and never silently mutates the original.
🧿 See safety.md
&expr creates a Ref<T>. ref.* dereferences it.
let x = 42
let r = &x
let value = r.* // 42📚 See 07-pointers.md
Slices and maps support bracket indexing:
let nums = [10, 20, 30]
let first = nums[0] // 10
let mut ages = Map.new<string, int>()
ages["Alice"] = 20
let age = ages["Alice"] // 20Bracket access panics if the index is out of bounds (slices) or returns the zero value if the key is missing (maps). For safe access that returns Option<T>, use .get(). See safety.md
let safe = nums.get(0) // Some(10)
let missing = nums.get(99) // None+=, -=, *=, /=, %=, &=, |=, ^=, &^=, <<=, >>= combine an operation with an assignment. The target must be mutable:
let mut count = 0
count += 1
let mut total = 10.0
total *= 1.5Mutation through a Ref<T> does not require mut on the binding.
📚 See 07-pointers.md
The ? operator propagates errors from Result and None from Option.
📚 See 09-error-handling.md
The as operator converts between numeric types and between string and Slice<byte> or Slice<rune>.
📚 See 02-types.md
← 02-types.md |
04-control-flow.md → |