Skip to content

Operators

Accepted

Accepted for V1 operator spelling, precedence, associativity, contract-backed operator surface, and deferred operator boundaries.

This page records source spelling, precedence, associativity, and lookup boundaries for operators. Operators are listed top to bottom in descending precedence. Semantic rules remain on the feature pages that own the values being operated on.

V1 has a closed contract-backed operator surface. Catalyst does not support arbitrary symbolic operator overloading, but selected built-in source forms lower through canonical prelude contract identities.

Precedence

a, b, and c stand for expressions. T and E stand for type expressions.

Prec. Operator Description Associativity
1 (a) grouping none
2 a(b) function or callable call left-to-right
a[b] indexing or slicing left-to-right
a.b member or namespace access left-to-right
a.* pointer dereference left-to-right
a.? forced optional unwrap left-to-right
3 &a address-of prefix
try a error propagation prefix
not a boolean negation prefix
-a numeric negation prefix
4 a * b, a / b, a % b multiplication, division, remainder left-to-right
5 a + b, a - b addition and subtraction left-to-right
6 a..b, a..=b half-open and inclusive-end range non-associative
7 a < b, a <= b, a > b, a >= b ordering comparison non-associative
8 a == b, a != b equality comparison non-associative
9 a and b short-circuit boolean or predicate conjunction left-to-right
10 a or b short-circuit boolean or predicate disjunction left-to-right
11 a orelse b optional defaulting unwrap left-to-right
a catch as err { ... }, a catch b error handling left-to-right
12 a = b assignment non-associative

When two operator families do not have a documented precedence relationship, code should use parentheses. The parser or sema should reject unclear mixes rather than silently choosing a surprising grouping.

Parsing, not availability

The table is a source parsing reference, not a promise that every operator is available for every type. Availability and lowering are owned by the relevant semantic pages.

Precedence and associativity are compile-time parsing concepts. They do not define runtime evaluation order. Evaluation order rules are documented on feature pages when they matter for moves, resource cleanup, or side effects.

Move

move place is a special ownership-transfer form, not a generic prefix operator over arbitrary expressions. The operand must be a movable local place as documented in Move Expressions.

When the moved value is used as a receiver, parenthesize the move:

var erased = (move concrete_box).into_dyn(Iterator(i32))

Moving the result of a method call is invalid in V1:

move concrete_box.into_dyn(Iterator(i32))

Type Operators

Type-level operator-like forms are parsed in type-expression contexts:

Form Description Canonical semantics
?T optional type Optional Types
T!E, T! error-return type Errors
A & B type or constraint intersection Type System
A | B enum union or error-set union Enums and Errors

These forms are documented separately from expression precedence because they appear where the parser expects types.

Composed error-set expressions in T!E must be parenthesized for readability:

Module!(ReadError | ParseError)

Unparenthesized mixes such as Module!ReadError | ParseError are rejected with a diagnostic that suggests the parenthesized form.

Contract-Backed Source Forms

These expression operators and operator-like source forms lower through canonical prelude contracts:

Source form Contract family Canonical semantics
a == b, a != b PartialEq(Rhs) Equality and Ordering Contracts
a < b, a <= b, a > b, a >= b PartialOrd(Rhs) Equality and Ordering Contracts
a + b, a - b, a * b, a / b, a % b, -a Add(Rhs, Out), Sub(Rhs, Out), Mul(Rhs, Out), Div(Rhs, Out), Rem(Rhs, Out), Neg(Out) Arithmetic Contracts
a[i] Indexable(T) Indexing and Sequence Contracts
a[i] = value MutableIndexable(T) Indexing and Sequence Contracts

Loop source selection is not an expression operator, but it is also contract-backed: for item in xs resolves through Iterator(Item), Iterable(Item), or MutableIterable(Item) as documented in Iterator Loop Sources.

Operator lowering is tied to canonical prelude contract identities, not arbitrary local bindings with the same names. Shadowing a prelude contract name does not change source-operator lowering.

Assignment

Assignment is a non-overloadable expression form:

place = expression

The right-hand side evaluates before the store. The assignment expression result is the assigned value observed through the destination place. If that result is consumed by value, ordinary copy and move rules apply.

Assignment does not associate:

a = b = c

Use explicit grouping when the result of one assignment is intentionally assigned again:

a = (b = c)

Assignments in conditions and assignment results bound to named locals are lintable because they are often accidental. Assignment to resource-owning places participates in ownership/live-overwrite.

Assignment-like syntax in declarations, aggregate construction, and destructuring is owned by those specific source forms:

  • binding = expr in declarations
  • .field = expr in aggregate construction
  • name = source_name in destructuring patterns

Compound assignment forms such as +=, -=, *=, /=, and %= are deferred from V1. Future compound assignment sugar and optional mutating override contracts are tracked in CEP-0055: Compound Assignment Operators.

Associativity

Arithmetic operators associate left-to-right:

a - b - c // (a - b) - c

Range operators are non-associative:

a..b..c // error

Comparison and equality operators do not chain:

a < b < c // error

Write the boolean relationship explicitly:

a < b and b < c

No implicit chaining

Range, ordering, and equality operators do not chain. Use parentheses or explicit boolean operators when combining comparisons.

Boolean and and or short-circuit left-to-right. For fact-bearing Type.Predicate values, and may preserve positive narrowing facts for the right-hand side and true branch; or and not have boolean behavior but do not add positive or negative narrowing facts in V1. See Predicate Reflection and Conditional Declarations.

Ranges

Range operators have lower precedence than arithmetic, so endpoint expressions can be written naturally:

a + 1..b - 1 // (a + 1)..(b - 1)

start..end creates a half-open range. start..=end creates an inclusive-end range.

Comparisons should not chain through ranges. Ambiguous expressions should require parentheses and produce diagnostics that ask for explicit grouping:

(a..b) == range
a < (b..c) // error unless a future type intentionally supports that comparison

Omitted-bound range forms are valid only in slicing contexts in V1:

items[start..]
items[..end]
items[..]

Standalone omitted-bound ranges are deferred.

Equality and Ordering

Equality and ordering are contract-backed source syntax:

a == b
a != b
a < b
a <= b
a > b
a >= b

These operators are not globally available for every type. Availability and lowering are documented in Equality and Ordering Contracts.

Comparison results involving unordered primitive floats follow the partial-order contract semantics: <, <=, >, and >= return false when operands are unordered.

Boolean Operators

Boolean operators use words, not symbolic spellings:

not ready
ready and valid
ready or fallback_allowed

and and or short-circuit. not applies to the following boolean operand.

The symbolic forms &&, ||, and !condition are not V1 boolean operators. Keeping boolean logic word-based makes it read consistently with and and or, and keeps ! visually reserved for error-return type syntax such as T!E and T!.

Optional Operators

orelse and .? are optional-specific source forms:

const label = maybe_label orelse "unknown"
const value = maybe_value.?

a orelse b unwraps one optional layer or evaluates the fallback when a is null. a.? forces an optional unwrap and traps on null in Checked safety mode. Optional type semantics, short-circuit behavior, and trap behavior are documented in Optional Types.

Arithmetic

Arithmetic operators are contract-backed source syntax:

a + b
a - b
a * b
a / b
a % b
-a

These operators are not globally available for every type. Availability, lowering, result-type selection, primitive numeric conformances, and user implementation rules are documented in Arithmetic Contracts.

Unary numeric negation applies to signed integers and floats. It is not part of a numeric literal token. A suffixed unsigned literal remains unsigned before unary negation is checked:

-1u8

Primitive integer overflow and floating-point type availability are documented in Built-In Types. Numeric coercions are documented in Numeric Types.

Bitwise and shift operator spellings are not part of the V1 expression operator surface. Their future contract-backed shape is tracked in CEP-0056: Bitwise and Shift Operators.