5Operators
5.1 Arithmetic
| Operator | Description |
|---|---|
+ - * / % | Add, subtract, multiply, divide, modulo |
- (unary) | Negation |
++ -- | Pre/postfix increment, decrement |
Integer division truncates toward zero. The prefix forms ++x, --x evaluate to the new value; the postfix forms x++, x-- evaluate to the old value (C semantics).
Overflow. Integer arithmetic wraps on overflow using two's-complement modular arithmetic, for both signed and unsigned types: the result is reduced modulo 2N for an N-bit type. There is no overflow trap and no automatic widening. For example, with i32:
mut x: i32 = 2147483647; // i32::MAX
x = x + 1; // wraps to -2147483648 (i32::MIN), no trap
This is a defined deterministic result, not undefined behavior, but it is silent: the language does not insert checks. Code that must detect overflow has to compare against the type's bounds before the operation. (Unsigned wrap is the usual mod 2N; e.g. 0u8 - 1u8 == 255.)
Division and modulo by zero are not checked by the compiler and fault at runtime (on typical targets the CPU raises SIGFPE); the signed i32::MIN / -1 / i32::MIN % -1 cases overflow the result and fault the same way. Guard the divisor when it can be zero.
5.2 Comparison
| Operator | Description |
|---|---|
== != | Equal / not equal |
< > <= >= | Ordering comparisons |
<=> | Three-way comparison (spaceship); yields an Ordering (Less / Equal / Greater) |
Comparison operators return boolean. On numeric types and pointers they emit native instructions; on user-defined types that implement Eq/Ord they are overloaded - a == b becomes a.equals(&b) and a < b becomes a.compare(&b).is_lt() (see operator overloading, section 11.6).
5.3 Logical
| Operator | Description |
|---|---|
&& | Logical AND (short-circuiting) |
|| | Logical OR (short-circuiting) |
! | Logical NOT (unary) |
5.4 Bitwise
| Operator | Description |
|---|---|
& | ^ | AND / OR / XOR |
~ | Bitwise NOT (unary) |
<< >> | Left / right shift (arithmetic on signed, logical on unsigned) |
5.5 Assignment
Only mut bindings can be assigned to.
| Operator | Description |
|---|---|
= | Simple assignment |
+= -= *= /= %= | Compound arithmetic |
&= |= ^= <<= >>= | Compound bitwise |
5.6 Other Operators
| Operator | Description |
|---|---|
-> | Return-type arrow. Not a member-access operator: . auto-dereferences pointers, so write p.field, never p->field. |
=> | Pattern-to-body separator inside match. |
:: | Scope resolution: static methods, enum variants, module members. |
? : | Ternary conditional. |
? (postfix) | Error propagation ("try"): on a Result/Option, yields the Ok/Some payload, else returns the Err/None from the enclosing function. |
?? | Null-coalescing: opt ?? fallback yields a Some's payload, else fallback (evaluated only when opt is None). |
|> <| | Pipeline: thread a value into a call (x |> f(a) => f(x, a); f(a) <| x => f(a, x)). |
as | Explicit type cast. |
. | Member access. |
& | Address-of (unary). |
* | Dereference (unary). |
sizeof(T) | Compile-time size of T in bytes. |
alignof(T) | Compile-time alignment of T in bytes. |
typeof(expr) | Compile-time type of expr, used in type position (decltype-style). |
new delete | Heap allocation / deallocation. |
Reserved.
?.and...in call position are recognised by the lexer but not yet lowered. See section 22. (The range operators../..=are fully lowered - see section 5.7.)
Pipeline (|>, <|). The pipeline operators thread a value into a call. x |> f is f(x); with an argument list the piped value is prepended - x |> f(a, b) is f(x, a, b). The backward form appends instead - f(a, b) <| x is f(a, b, x). Pipes are left-associative, so x |> f |> g is g(f(x)). They are a compile-time rewrite to an ordinary call, with no runtime cost.
const out: int = data |> parse |> validate(strict); // validate(parse(data), strict)
Null-coalescing (??). opt ?? fallback unwraps an Option<T> to its T, substituting fallback when it is None. The left operand is evaluated once and fallback only when needed. ?? is right-associative and binds looser than the pipes, so a chain reads as a ?? (b ?? c): every operand but the last is an Option<T>, and the final c is the bare T.
const port: u16 = config_port() ?? env_port() ?? 8080;
Error propagation (?). A postfix ? on a Result<T, E> evaluates to T when the value is Ok, and otherwise returns that Err(e) unchanged from the enclosing function; on an Option<T> it yields T for Some and returns None. The enclosing function's return type must be a matching Result / Option. It is the concise form of a match that re-returns the error.
function load(path: string) -> Result<Config, IoError> {
const text: String = read_file(path)?; // returns Err(e) on failure
return Result::Ok(parse_config(text));
}
Type-of (typeof). typeof(expr) resolves to the static type of expr and is used in type position - anywhere a type annotation is expected: variable bindings, pointer/array/optional wrappers, generic arguments, and as cast targets. It is a compile-time construct that names a type, not a value, so it cannot appear where a value is expected. expr is only type-checked, never evaluated.
const x: i32 = read_count();
const y: typeof(x) = 0; // y : i32
const p: typeof(x)* = &x; // p : i32*
const n: i64 = 5;
const back = n as typeof(x); // back : i32
5.7 Operator Precedence
From lowest to highest:
| Level | Operators | Associativity |
|---|---|---|
| 1 | = += -= *= /= %= &= |= ^= <<= >>= | Right |
| 2 | ?? (null-coalescing) | Right |
| 3 | |> <| (pipeline) | Left |
| 4 | ? : (ternary) | Right |
| 5 | .. ..= (range) | Left |
| 6 | || | Left |
| 7 | && | Left |
| 8 | | | Left |
| 9 | ^ | Left |
| 10 | & | Left |
| 11 | == != | Left |
| 12 | < > <= >= <=> | Left |
| 13 | << >> | Left |
| 14 | + - | Left |
| 15 | * / % | Left |
| 16 | as | Left |
| 17 | - ! & * ~ ++ -- (unary prefix), new, delete | Right |
| 18 | () [] . ? (postfix try) ++ -- (postfix) | Left |
as sits between multiplication and unary, so x * y as i64 casts y, not the product. Use parentheses if you mean (x * y) as i64.
The range operators .. / ..= bind looser than every arithmetic and comparison operator, so a + 1 .. b * 2 is (a + 1) .. (b * 2). They desugar at parse time to Range::new / RangeInclusive::new (see section 6.3).