Advanced Operators

Beyond the basic operators, Swift provides advanced operators for low-level bit manipulation, controlled overflow arithmetic, and — most powerfully — the ability to define your own operators and overload existing ones for your own types. This chapter also introduces result builders, which use operator-like composition to build values from nested expressions.

Note: unlike arithmetic operators in C, Swift's arithmetic operators don't overflow by default. Overflow is trapped and reported as an error. To opt into overflow behavior, you use the dedicated overflow operators described below.

Bitwise operators

Bitwise operators let you manipulate the individual raw data bits within a data structure. Swift supports the five standard bitwise operators from C.

The bitwise NOT operator (~) is a prefix operator that inverts all bits in a number:

let initialBits: UInt8 = 0b00001111
let invertedBits = ~initialBits   // 0b11110000 → 240

~ flips every bit.

The bitwise AND (&) keeps a bit only where both inputs have it set; OR (|) sets a bit where either input has it; XOR (^) sets a bit where exactly one input has it:

let a: UInt8 = 0b11111100
let b: UInt8 = 0b00111111

let and = a & b   // 0b00111100 → 60
let or  = a | b   // 0b11111111 → 255
let xor = a ^ b   // 0b11000011 → 195

Bitwise AND, OR, and XOR combine two values bit by bit.

The shift operators (<< and >>) move all of a number's bits left or right by a number of positions. For unsigned integers, shifting is logical: bits shifted off the end are discarded, and zeros are inserted on the other side. For signed integers, the right shift is arithmetic — it fills with copies of the sign bit to preserve the number's sign.

let pink: UInt32 = 0xCC6699
let red   = (pink & 0xFF0000) >> 16   // 0xCC → 204
let green = (pink & 0x00FF00) >> 8    // 0x66 → 102
let blue  =  pink & 0x0000FF          // 0x99 → 153

Masking and shifting to extract color channels from a hex value.

Overflow operators

If you try to insert a value into an integer that can't hold it, Swift reports an error rather than wrapping around. When you specifically want the value to truncate, opt in with the three overflow operators: overflow addition (&+), overflow subtraction (&-), and overflow multiplication (&*).

var unsignedOverflow = UInt8.max   // 255, the largest UInt8
unsignedOverflow = unsignedOverflow &+ 1   // wraps to 0

var low = UInt8.min          // 0, the smallest UInt8
low = low &- 1                  // wraps to 255

// Without &, `UInt8.max + 1` traps at runtime.

Overflow operators wrap around the value's bounds instead of trapping.

Precedence and associativity

Operator precedence gives some operators higher priority, so they're applied first. Associativity defines how operators of the same precedence are grouped — either from the left or from the right. Swift's multiplication and division operators have higher precedence than addition and subtraction, and within the same group they're left-associative:

let result = 2 + 3 % 4 * 5
// % and * bind tighter than +, and group left-to-right:
// 2 + ((3 % 4) * 5) = 2 + (3 * 5) = 17

Precedence and associativity determine grouping without parentheses.

Tip: Swift's precedence rules are simpler and more predictable than C's, but they may differ from languages you know. When in doubt, add parentheses to make your intent explicit and readable.

Operator methods

Classes and structures can provide their own implementations of existing operators — this is called overloading. You implement an operator as a type method (typically static):

struct Vector2D {
    var x = 0.0, y = 0.0
}

extension Vector2D {
    static func + (left: Vector2D, right: Vector2D) -> Vector2D {
        Vector2D(x: left.x + right.x, y: left.y + right.y)
    }
}

let combined = Vector2D(x: 3, y: 1) + Vector2D(x: 2, y: 4)
// combined is (x: 5, y: 5)

An infix + operator method for a custom type.

Prefix and postfix operators

Operators that take a single operand are unary. You declare them with the prefix or postfix modifier to say which side the operand sits on:

extension Vector2D {
    static prefix func - (vector: Vector2D) -> Vector2D {
        Vector2D(x: -vector.x, y: -vector.y)
    }
}

let positive = Vector2D(x: 3, y: 4)
let negative = -positive   // (x: -3, y: -4)

A unary prefix minus that negates both components.

Compound assignment operators

Compound assignment operators combine assignment (=) with another operation. You mark the left input as inout, because its value is modified directly inside the method:

extension Vector2D {
    static func += (left: inout Vector2D, right: Vector2D) {
        left = left + right
    }
}

var original = Vector2D(x: 1, y: 2)
original += Vector2D(x: 3, y: 4)   // original is now (4, 6)

+= takes its left operand inout so it can be mutated in place.

Watch out: you can't overload the default assignment operator (=) itself, nor the ternary conditional operator (a ? b : c). These are reserved by the language.

Equivalence operators

To support == and !=, conform to the Equatable protocol. For most types Swift can synthesize the conformance automatically, but you can implement == yourself when you need custom logic (Swift derives != for you):

extension Vector2D: Equatable {
    static func == (left: Vector2D, right: Vector2D) -> Bool {
        left.x == right.x && left.y == right.y
    }
}

let same = Vector2D(x: 1, y: 2) == Vector2D(x: 1, y: 2)   // true

Conforming to Equatable with a custom == operator method.

Custom operators

You can declare and implement entirely new operators in addition to the standard ones. New operators are declared at global scope using the operator keyword, marked prefix, infix, or postfix:

prefix operator +++

extension Vector2D {
    static prefix func +++ (vector: inout Vector2D) -> Vector2D {
        vector += vector   // a "doubling" operator
        return vector
    }
}

var toDouble = Vector2D(x: 1, y: 4)
let doubled = +++toDouble   // both are now (2, 8)

Declaring and defining a brand-new prefix operator.

Precedence groups

When you define a custom infix operator, it belongs to a precedence group that tells Swift how it relates to other infix operators in precedence and associativity. If you don't specify one, it joins the default group, which has higher precedence than the ternary operator. You define your own group with the precedencegroup keyword:

precedencegroup VectorAdditionPrecedence {
    associativity: left
    higherThan: AdditionPrecedence
}

infix operator +-: VectorAdditionPrecedence

extension Vector2D {
    static func +- (left: Vector2D, right: Vector2D) -> Vector2D {
        Vector2D(x: left.x + right.x, y: left.y - right.y)
    }
}

let v = Vector2D(x: 1, y: 2) +- Vector2D(x: 3, y: 4)   // (4, -2)

A precedence group sets associativity and how the operator ranks against others.

Each group can declare it's higherThan or lowerThan existing groups and choose left, right, or none associativity. You don't specify a precedence group for prefix or postfix operators — if you apply both a prefix and a postfix operator to the same value, the postfix one is applied first.

Result builders

A result builder is a type you define that adds syntax for creating nested data — like a tree or list — in a natural, declarative way. Result builders are how SwiftUI lets you list views inside a VStack without commas or array brackets. You create one by applying the @resultBuilder attribute to a type and providing buildBlock (and optionally buildOptional, buildEither, buildArray, and others):

@resultBuilder
struct StringBuilder {
    static func buildBlock(_ parts: String...) -> String {
        parts.joined(separator: "\n")
    }
    static func buildOptional(_ part: String?) -> String {
        part ?? ""
    }
    static func buildEither(first: String) -> String { first }
    static func buildEither(second: String) -> String { second }
}

A minimal result builder that joins strings, with support for if/else.

Apply the builder as a custom attribute on a function or closure parameter. Inside that scope, the builder transforms each statement into calls to its build methods, so you can use if, if let, and loops naturally:

func makeGreeting(@StringBuilder _ content: () -> String) -> String {
    content()
}

let excited = true
let message = makeGreeting {
    "Hello"
    if excited {
        "Welcome aboard!"
    } else {
        "Glad you're here."
    }
    "— The Team"
}
// → "Hello\nWelcome aboard!\n— The Team"

The builder turns plain statements (including control flow) into a single result.

Tip: you've already been using a result builder. SwiftUI's @ViewBuilder is exactly this mechanism — it's what lets the contents of a stack or list read like a simple sequence of views.

Wrapping up

Advanced operators take you from manipulating raw bits to shaping the language itself. Bitwise and overflow operators handle low-level numeric work; operator methods and custom operators let your own types read as naturally as built-in ones; precedence groups control how expressions combine; and result builders turn nested statements into rich values — the foundation of declarative APIs like SwiftUI.