Are you an LLM? Read llms.txt for a summary of the docs, or llms-full.txt for the full context.
Skip to content

Operators

Operators are syntax sugar over built-in functions.

Binary operators

<arithmetic_binary_operator> ::=
    | "+" | "-" | "*" | "/" | "%" | "**" ;

<bitwise_binary_operator> ::=
    | "&" | "|" | "^" | "<<" | ">>" ;

<comparison_binary_operator> ::=
    | "==" | "!=" | "<" | "<=" | ">" | ">=" ;

<logical_binary_operator> ::=
    | "&&" | "||" ;

<compound_assignment_operator> ::=
    | "+=" | "-=" | "*=" | "/=" | "%=" | "**="
    | "&=" | "|=" | "^=" | "<<=" | ">>=" ;

<binary_operator> ::=
    | <arithmetic_binary_operator>
    | <bitwise_binary_operator>
    | <comparison_binary_operator>
    | <logical_binary_operator>
    | <compound_assignment_operator> ;

Unary operators

<arithmetic_unary_operator> ::= "-" ;

<bitwise_unary_operator> ::= "~" ;

<logical_unary_operator> ::= "!" ;

<unary_operator> ::=
    | <arithmetic_unary_operator>
    | <bitwise_unary_operator>
    | <logical_unary_operator> ;

Precedence

The expression parser uses precedence climbing (Pratt parsing). Lower numbers bind less tightly:

PrecedenceOperatorsAssociativity
0=Right
1||Left
2&&Left
3== !=Left
4< > <= >=Left
5| (bitwise OR)Left
6^ (bitwise XOR)Left
7& (bitwise AND)Left
8<< >>Left
9+ -Left
10* / %Left
11**Right

The ternary operator (? :) is parsed after the Pratt binary expression, with right-to-left associativity.

Compound assignment operators (+=, -=, etc.) are parsed as binary operations and produce Expr::Binary nodes with the corresponding BinOp variant.

Index

<index_operator> ::= "[" <expr> "]" ;

The index operator [] dispatches to the Index trait. Any type implementing Index<Idx, Output> can be indexed with value[key].

Semantics

OperatorTypesBehaviorPanic case
+integerschecked additionoverflow
- (binary)integerschecked subtractionunderflow
- (unary)integerschecked negationoverflow
*integerschecked multiplicationoverflow
/integerschecked divisiondivide by zero
%integerschecked modulusdivide by zero
**integersexponentiation
&integersbitwise AND
|integersbitwise OR
~integersbitwise NOT
^integersbitwise XOR
>>integersbitwise shift right
<<integersbitwise shift left
==anyequality
!=anyinequality
&&booleanslogical AND
||booleanslogical OR
!booleanslogical NOT
>integersgreater than
>=integersgreater than or equal
<integersless than
<=integersless than or equal
[]anyindex

Checked arithmetic

The +, -, and * operators are checked by default: they revert on overflow or underflow. The compiler's range analysis pass can elide these checks when it can statically prove the operation is safe (e.g., adding two values whose combined upper bound fits in the type). This happens automatically at optimization level O1 and above.

For performance-critical code where overflow is known to be impossible, the standard library provides unchecked variants via the UnsafeAdd, UnsafeSub, and UnsafeMul traits (see Standard Library).

Operator overloading

Direct operator overloading is disallowed. However, the following operators can be customized for user-defined types by implementing the corresponding standard library trait from std::ops:

OperatorTraitMethod(s)
+Addadd(self, rhs)
-Subsub(self, rhs)
*Mulmul(self, rhs)
/Divdiv(self, rhs)
%Modmod_(self, rhs)
==Eqeq(self, rhs)
< <= > >=Ordlt le gt ge
[]Index<Idx, Output>index(self, idx)

Bitwise and logical operators (&, |, ^, ~, <<, >>, &&, ||) are currently primitives-only. Exponentiation (**) is also primitives-only. Trait-based overloading for these operators may be added in the future.

Example:

use std::ops::Add;
use std::ops::Eq;
 
type Wrapper = { value: u256 };
 
impl Wrapper: Add {
    fn add(self, rhs: Self) -> (Self) {
        Wrapper { value: self.value + rhs.value }
    }
}
 
impl Wrapper: Eq {
    fn eq(self, rhs: Self) -> (bool) {
        self.value == rhs.value
    }
}
 
// Now `Wrapper { value: 1 } + Wrapper { value: 2 }` works.