Zig Operators
Operators and expressions are fundamental components in programming languages used to perform various operations.
In Zig, operators can be divided into several categories, including arithmetic operators, relational operators, logical operators, bitwise operators, assignment operators, and other operators.
The following are detailed descriptions and examples of each type of operator.
Arithmetic Operators
| Operator | Description |
|---|---|
| + | Addition |
| - | Subtraction |
| * | Multiplication |
| / | Division |
| % | Remainder (modulo operation) |
Example
const std = @import("std");
pub fn main() void {
const a: i32 = 5;
const b: i32 = 3;
const add: i32 = a + b;
const subtract: i32 = a - b;
const multiply: i32 = a * b;
const divide: i32 = a / b;
const remainder: i32 = a % b;
std.debug.print("a + b = {}\n", .{add});
std.debug.print("a - b = {}\n", .{subtract});
std.debug.print("a * b = {}\n", .{multiply});
std.debug.print("a / b = {}\n", .{divide});
std.debug.print("a % b = {}\n", .{remainder});
}
pub fn main() void {
const a: i32 = 5;
const b: i32 = 3;
const add: i32 = a + b;
const subtract: i32 = a - b;
const multiply: i32 = a * b;
const divide: i32 = a / b;
const remainder: i32 = a % b;
std.debug.print("a + b = {}\n", .{add});
std.debug.print("a - b = {}\n", .{subtract});
std.debug.print("a * b = {}\n", .{multiply});
std.debug.print("a / b = {}\n", .{divide});
std.debug.print("a % b = {}\n", .{remainder});
}
The compiled output is:
a + b = 8 a - b = 2 a * b = 15 a / b = 1 a % b = 2
Relational Operators
| Operator | Description |
|---|---|
| == | Equal to |
| != | Not equal to |
| > | Greater than |
| < | Less than |
| >= | Greater than or equal to |
| <= | Less than or equal to |
Example
const std = @import("std");
pub fn main() void {
const a: i32 = 5;
const b: i32 = 3;
const equal: bool = a == b;
const not_equal: bool = a != b;
const greater: bool = a > b;
const less: bool = a < b;
const greater_equal: bool = a >= b;
const less_equal: bool = a <= b;
std.debug.print("a == b: {}\n", .{equal});
std.debug.print("a != b: {}\n", .{not_equal});
std.debug.print("a > b: {}\n", .{greater});
std.debug.print("a < b: {}\n", .{less});
std.debug.print("a >= b: {}\n", .{greater_equal});
std.debug.print("a <= b: {}\n", .{less_equal});
}
pub fn main() void {
const a: i32 = 5;
const b: i32 = 3;
const equal: bool = a == b;
const not_equal: bool = a != b;
const greater: bool = a > b;
const less: bool = a < b;
const greater_equal: bool = a >= b;
const less_equal: bool = a <= b;
std.debug.print("a == b: {}\n", .{equal});
std.debug.print("a != b: {}\n", .{not_equal});
std.debug.print("a > b: {}\n", .{greater});
std.debug.print("a < b: {}\n", .{less});
std.debug.print("a >= b: {}\n", .{greater_equal});
std.debug.print("a <= b: {}\n", .{less_equal});
}
The compiled output is:
a == b: false a != b: true a > b: true a < b: false a >= b: true a <= b: false
Logical Operators
| Operator | Description |
|---|---|
| and | Logical AND |
| or | Logical OR |
| ! | Logical NOT |
Example
const std = @import("std");
pub fn main() void {
const a: bool = true;
const b: bool = false;
const and_result: bool = a and b;
const or_result: bool = a or b;
const not_result: bool = !a;
std.debug.print("a and b: {}\n", .{and_result}); // false
std.debug.print("a or b: {}\n", .{or_result}); // true
std.debug.print("!a: {}\n", .{not_result}); // false
}
pub fn main() void {
const a: bool = true;
const b: bool = false;
const and_result: bool = a and b;
const or_result: bool = a or b;
const not_result: bool = !a;
std.debug.print("a and b: {}\n", .{and_result}); // false
std.debug.print("a or b: {}\n", .{or_result}); // true
std.debug.print("!a: {}\n", .{not_result}); // false
}
The compiled output is:
a and b: false a or b: true !a: false
Bitwise Operators
| Operator | Description |
|---|---|
| & | Bitwise AND |
| | | Bitwise OR |
| ^ | Bitwise XOR |
| ~ | Bitwise NOT |
| << | Left shift |
| >> | Right shift |
Example
const std = @import("std");
pub fn main() void {
const a: i32 = 5; // 0101
const b: i32 = 3; // 0011
const bit_and: i32 = a & b; // 0001
const bit_or: i32 = a | b; // 0111
const bit_xor: i32 = a ^ b; // 0110
const bit_not: i32 = ~a; // 11111111111111111111111111111010
const left_shift: i32 = a << 1; // 1010
const right_shift: i32 = a >> 1; // 0010
std.debug.print("a & b: {}\n", .{bit_and});
std.debug.print("a | b: {}\n", .{bit_or});
std.debug.print("a ^ b: {}\n", .{bit_xor});
std.debug.print("~a: {}\n", .{bit_not});
std.debug.print("a << 1: {}\n", .{left_shift});
std.debug.print("a >> 1: {}\n", .{right_shift});
}
pub fn main() void {
const a: i32 = 5; // 0101
const b: i32 = 3; // 0011
const bit_and: i32 = a & b; // 0001
const bit_or: i32 = a | b; // 0111
const bit_xor: i32 = a ^ b; // 0110
const bit_not: i32 = ~a; // 11111111111111111111111111111010
const left_shift: i32 = a << 1; // 1010
const right_shift: i32 = a >> 1; // 0010
std.debug.print("a & b: {}\n", .{bit_and});
std.debug.print("a | b: {}\n", .{bit_or});
std.debug.print("a ^ b: {}\n", .{bit_xor});
std.debug.print("~a: {}\n", .{bit_not});
std.debug.print("a << 1: {}\n", .{left_shift});
std.debug.print("a >> 1: {}\n", .{right_shift});
}
The compiled output is:
a & b: 1 a | b: 7 a ^ b: 6 ~a: -6 a << 1: 10 a >> 1: 2
Assignment Operators
| Operator | Description |
|---|---|
| = | Assignment |
| += | Addition assignment |
| -= | Subtraction assignment |
| *= | Multiplication assignment |
| /= | Division assignment |
| %= | Remainder assignment |
| &= | Bitwise AND assignment |
| |= | Bitwise OR assignment |
| ^= | Bitwise XOR assignment |
| <<= | Left shift assignment |
| >>= | Right shift assignment |
Example
const std = @import("std");
pub fn main() void {
var a: i32 = 5;
const b: i32 = 3;
a += b; // equivalent to a = a + b;
std.debug.print("a += b: {}\n", .{a});
a -= b; // equivalent to a = a - b;
std.debug.print("a -= b: {}\n", .{a});
a *= b; // equivalent to a = a * b;
std.debug.print("a *= b: {}\n", .{a});
a = @divTrunc(a, b); // equivalent to a = a / b;
std.debug.print("a /= b: {}\n", .{a});
a = @mod(a, b); // equivalent to a = a % b;
std.debug.print("a %= b: {}\n", .{a});
a &= b; // equivalent to a = a & b;
std.debug.print("a &= b: {}\n", .{a});
a |= b; // equivalent to a = a | b;
std.debug.print("a |= b: {}\n", .{a});
a ^= b; // equivalent to a = a ^ b;
std.debug.print("a ^= b: {}\n", .{a});
a <<= 1; // equivalent to a = a << 1;
std.debug.print("a <<= 1: {}\n", .{a});
a >>= 1; // equivalent to a = a >> 1;
std.debug.print("a >>= 1: {}\n", .{a});
}
pub fn main() void {
var a: i32 = 5;
const b: i32 = 3;
a += b; // equivalent to a = a + b;
std.debug.print("a += b: {}\n", .{a});
a -= b; // equivalent to a = a - b;
std.debug.print("a -= b: {}\n", .{a});
a *= b; // equivalent to a = a * b;
std.debug.print("a *= b: {}\n", .{a});
a = @divTrunc(a, b); // equivalent to a = a / b;
std.debug.print("a /= b: {}\n", .{a});
a = @mod(a, b); // equivalent to a = a % b;
std.debug.print("a %= b: {}\n", .{a});
a &= b; // equivalent to a = a & b;
std.debug.print("a &= b: {}\n", .{a});
a |= b; // equivalent to a = a | b;
std.debug.print("a |= b: {}\n", .{a});
a ^= b; // equivalent to a = a ^ b;
std.debug.print("a ^= b: {}\n", .{a});
a <<= 1; // equivalent to a = a << 1;
std.debug.print("a <<= 1: {}\n", .{a});
a >>= 1; // equivalent to a = a >> 1;
std.debug.print("a >>= 1: {}\n", .{a});
}
The compiled output is:
a += b: 8 a -= b: 5 a *= b: 15 a /= b: 5 a %= b: 2 a &= b: 2 a |= b: 3 a ^= b: 0 a <<= 1: 0 a >>= 1: 0
Other Operators
| Operator | Description |
|---|---|
| ++ | Increment |
| -- | Decrement |
Example
const std = @import("std");
pub fn main() void {
var a: i32 = 5;
a += 1; // Zig does not have the ++ operator, use += 1 instead
std.debug.print("a += 1: {}\n", .{a});
a -= 1; // Zig does not have the -- operator, use -= 1 instead
std.debug.print("a -= 1: {}\n", .{a});
}
pub fn main() void {
var a: i32 = 5;
a += 1; // Zig does not have the ++ operator, use += 1 instead
std.debug.print("a += 1: {}\n", .{a});
a -= 1; // Zig does not have the -- operator, use -= 1 instead
std.debug.print("a -= 1: {}\n", .{a});
}
The compiled output is:
a += 1: 6 a -= 1: 5
Operator Precedence
The following is a list of Zig operator precedence, arranged from highest to lowest:
| Precedence | Operator | Description |
|---|---|---|
| 1 | [] | Subscript operation, array or pointer access |
| 1 | . | Member access |
| 2 | fn_call() | Function call |
| 2 | @builtin() | Built-in function call |
| 3 | ! | Error propagation |
| 3 | ? | Optional value unwrapping |
| 4 | * & | Pointer dereference and address operations |
| 5 | + - | Unary plus and minus |
| 6 | ~ | Bitwise NOT |
| 7 | * / % | Multiplication, division, remainder |
| 8 | + - | Addition, subtraction |
| 9 | << >> | Bitwise left shift, right shift |
| 10 | & | Bitwise AND |
| 11 | ^ | Bitwise XOR |
| 12 | ` | ` |
| 13 | == != | Equal, not equal |
| 13 | < <= > >= | Less than, less than or equal, greater than, greater than or equal |
| 14 | and | Logical AND |
| 15 | or | Logical OR |
| 16 | orelse | Or else return another value |
| 17 | catch | Capture error |
| 18 | = | Assignment |
| 19 | -> | Closure function body or return value type indicator |
Notes:
- Associativity: Usually Zig operators are left-associative, but you can refer to the documentation based on the specific function of the operator.
- Notes: The operators in Zig are designed to be simple and clear, avoiding complex implicit behaviors found in many languages, such as no implicit type conversion.
The following example shows how operator precedence affects the order of evaluation of expressions:
Example
const std = @import("std");
pub fn main() void {
const a: i32 = 5;
const b: i32 = 3;
const c: i32 = 2;
// Multiplication has higher precedence than addition
const result1: i32 = a + b * c; // 5 + (3 * 2) = 11
std.debug.print("a + b * c = {}\n", .{result1});
// Use parentheses to change precedence
const result2: i32 = (a + b) * c; // (5 + 3) * 2 = 16
std.debug.print("(a + b) * c = {}\n", .{result2});
// Comparison operators have higher precedence than logical operators
const result3: bool = a > b and b > c; // (5 > 3) and (3 > 2) = true
std.debug.print("a > b and b > c = {}\n", .{result3});
// Logical NOT has higher precedence than logical AND
const result4: bool = !(a > b) and b > c; // !(5 > 3) and (3 > 2) = false
std.debug.print("!(a > b) and b > c = {}\n", .{result4});
}
pub fn main() void {
const a: i32 = 5;
const b: i32 = 3;
const c: i32 = 2;
// Multiplication has higher precedence than addition
const result1: i32 = a + b * c; // 5 + (3 * 2) = 11
std.debug.print("a + b * c = {}\n", .{result1});
// Use parentheses to change precedence
const result2: i32 = (a + b) * c; // (5 + 3) * 2 = 16
std.debug.print("(a + b) * c = {}\n", .{result2});
// Comparison operators have higher precedence than logical operators
const result3: bool = a > b and b > c; // (5 > 3) and (3 > 2) = true
std.debug.print("a > b and b > c = {}\n", .{result3});
// Logical NOT has higher precedence than logical AND
const result4: bool = !(a > b) and b > c; // !(5 > 3) and (3 > 2) = false
std.debug.print("!(a > b) and b > c = {}\n", .{result4});
}
The compiled output is:
a + b * c = 11 (a + b) * c = 16 a > b and b > c = true !(a > b) and b > c = falseOther Extensions