Skip to content

Operators

Precedence decides how an expression groups when it mixes operators, from lowest to highest:

PrecedenceOperatorsDescription
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
8asType conversion
9- ! ^ &Prefix (negation, not, bitwise not, reference)
10. () [] ? .*Postfix (access, call, index, propagate, deref)

All binary operators are left-associative, so a repeated operator groups from the left.

a + b * cparses as a + (b * c)
a && b || cparses as (a && b) || c
a + b |> fparses as f(a + b)
0..1 + 2parses as 0..(1 + 2)

+, -, *, /, % require both operands to be the same numeric type. An untyped numeric literal adapts to the other operand, and unary - negates a number.

+ also concatenates strings:

let greeting = "hello" + ", " + "world"

== and != compare two values of the same comparable type. <, >, <=, >= compare numeric types and strings. All of them return bool.

let same = 2 == 2true
let ordered = "apple" < "banana"true, compared byte by byte

&& 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 = ^value

Arrays and slices support integer indexing:

let nums = [10, 20, 30]
let first = nums[0]10
let fixed: Array<int, 3> = [10, 20, 30]
let second = fixed[1]20

Maps support key indexing:

let mut ages = Map.new<string, int>()
ages["Alice"] = 20
let age = ages["Alice"]20

Bracket access on an array or slice panics if the index is out of bounds. Bracket access on maps returns the zero value if the key is missing. If the map’s value type has no zero value (e.g. Ref<T>), bracket reads are rejected at compile time.

For access that cannot panic, use .get(), which returns an Option<T>.

Range indexing is available only on slices. To range-index an array, first copy its elements into a slice:

let tail = fixed.to_slice()[1..]
SyntaxTypeDescription
start..endRange<T>Exclusive upper bound
start..=endRangeInclusive<T>Inclusive upper bound
start..RangeFrom<T>No upper bound
..endRangeTo<T>Exclusive, no lower bound
..=endRangeToInclusive<T>Inclusive, no lower bound

The .. and ..= operators build these values, which drive a for loop or index a slice:

let slice = items[1..4]elements at indices 1, 2, 3

Slice 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.

&expr points at a value instead of copying it, giving a Ref<T> that several places can hold at once. ref.* dereferences it, reading back the value it points at.

let x = 42
let r = &x
let value = r.*42

The pipeline operator |> passes the left side as the first argument to the function on the right.

x |> fequivalent to f(x)
x |> f(y)equivalent to f(x, y)
x |> f(y, z)equivalent to 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))

Lambdas are not allowed as pipeline targets.

The ? operator propagates a failure.

The as operator converts between numeric types.

= assigns to a mutable target.

let mut name = "Alice"
name = "Bob"

+=, -=, *=, /=, %=, &=, |=, ^=, &^=, <<=, >>= combine an operation with an assignment.

let mut count = 0
count += 1
let mut total = 10.0
total *= 1.5