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

OperatorDescription
+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});
}

The compiled output is:

a + b = 8
a - b = 2
a * b = 15
a / b = 1
a % b = 2

Relational Operators

OperatorDescription
==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});
}

The compiled output is:

a == b: false
a != b: true
a > b: true
a < b: false
a >= b: true
a <= b: false

Logical Operators

OperatorDescription
andLogical AND
orLogical 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
}

The compiled output is:

a and b: false
a or b: true
!a: false

Bitwise Operators

OperatorDescription
&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});
}

The compiled output is:

a & b: 1
a | b: 7
a ^ b: 6
~a: -6
a << 1: 10
a >> 1: 2

Assignment Operators

OperatorDescription
=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});
}

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

OperatorDescription
++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});
}

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:

PrecedenceOperatorDescription
1[]Subscript operation, array or pointer access
1.Member access
2fn_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
14andLogical AND
15orLogical OR
16orelseOr else return another value
17catchCapture 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});
}

The compiled output is:

a + b * c = 11
(a + b) * c = 16
a > b and b > c = true
!(a > b) and b > c = false
Other Extensions