Operators

This section documents the Swift 6 language mode as shipped by Swift 6.3, as published in The Swift Programming Language at docs.swift.org, which is the reference these pages are written and verified against. 6.4-beta-only features are always flagged as such — never presented as baseline.

This content was generated with the assistance of AI and should be verified against docs.swift.org before being relied on in production.

This section’s bibliography lists the reference material consulted while preparing these pages.

Swift’s operator set is close to what a C-family language offers, plus range operators purpose-built for loops and slicing, and every operator’s exact precedence is queryable from a single, extensible table rather than memorized folklore.

Assignment

let b = 10
var a = 5
a = b                       // a is now 10

let (x, y) = (1, 2)         // assignment also decomposes a tuple into multiple constants

Unlike C’s, Swift’s assignment operator does not itself return a value — if x = y { …​ } is a compile error, which rules out the classic =-for-== typo entirely.

Arithmetic and Remainder

let sum = 1 + 2            // 3
let difference = 5 - 3     // 2
let product = 3 * 2        // 6
let quotient = 10.0 / 2.5  // 4.0

print("hello, " + "world")  // + is also overloaded for String concatenation

let remainder = 9 % 4       //  1
print(-9 % 4)                // -1 -- result takes the sign of the dividend, matching C's `%`
print(4 % -9)                //  4
print(8.0.truncatingRemainder(dividingBy: 2.5))   // 0.5 -- % has no floating-point overload; use this instead

Unary plus (x`) is a no-op provided purely for symmetry with unary minus (`-x`); overflow on `/-/// *traps by default (a runtime crash), unlike C’s silent wraparound — see the &+/&-/&* overflow operators in Advanced Operators for the opt-in wrapping versions.

Compound Assignment

var a = 1
a += 2          // a = a + 2, now 3
a -= 1
a *= 4
a /= 2

A compound-assignment operator’s result cannot itself be used as a value — let b = (a += 2) does not compile — which differs from C’s compound assignment.

Comparison Operators

print(1 == 1)   // true
print(2 != 1)   // true
print(2 > 1)    // true
print(1 < 2)    // true
print(1 >= 1)   // true
print(2 <= 1)   // false

let tuple1 = (1, "zebra")
let tuple2 = (2, "apple")
print(tuple1 < tuple2)      // true -- compares 1 against 2 first; the strings are never reached
print((1, "zebra") < (1, "apple"))   // false -- first elements tie, so the second elements decide

Tuples compare left to right, element by element, short-circuiting at the first pair that differs; Swift provides these comparison operators for tuples of up to six elements out of the box, and every element’s type must itself be Comparable (or, for ==/!=, Equatable).

The Ternary Conditional Operator

let contentHeight = 40
let hasHeader = true
let rowHeight = hasHeader ? contentHeight + 50 : contentHeight + 20   // 90

question ? answer1 : answer2 is shorthand for a two-branch if/else that produces a value; exactly one of answer1/answer2 is evaluated. Nested ternaries read poorly fast — prefer a plain if/else expression (Swift 5.9+) or a switch once a condition gets past one level.

Nil-Coalescing

let defaultColorName = "red"
let userDefinedColorName: String? = nil

let colorNameToUse = userDefinedColorName ?? defaultColorName   // "red"

a ?? b unwraps a if non-nil, otherwise evaluates b; it is covered in full, alongside every other way to work with optionals, in Optionals.

Range Operators

for index in 1...5 { print(index) }        // 1 2 3 4 5 -- closed range, includes both ends

let names = ["Anna", "Alex", "Brian", "Jack"]
for i in 0..<names.count { print(names[i]) }   // half-open range, excludes the upper bound -- the usual array-index range

print(names[2...])          // ["Brian", "Jack"]   -- one-sided range: from index 2 to the end
print(names[...2])          // ["Anna", "Alex", "Brian"]   -- one-sided range: from the start through index 2
print(names[..<2])          // ["Anna", "Alex"]            -- one-sided, half-open

for name in names[1...2] { print(name) }    // one-sided/closed ranges work as subscripts and as sequences alike

print((1...5).contains(3))  // true -- a range is a real value, testable with `contains`
// let backwards = 5...1    // runtime error: a closed range's lower bound must be <= its upper bound

A closed range (a…​b) includes both endpoints; a half-open range (a..<b) excludes the upper bound and is the natural fit for zero-based array indices; a one-sided range (a…​, …​b, ..<b) is a range Swift infers as far as context allows — typically "to the end" or "from the start" of a collection.

Logical Operators

let enteredDoorCode = true
let passedRetinaScan = false

if enteredDoorCode && passedRetinaScan {
    print("Welcome!")
} else {
    print("ACCESS DENIED")               // printed -- && short-circuits, evaluated left to right
}

let hasDoorKey = false
let knowsOverridePassword = true
if hasDoorKey || knowsOverridePassword {
    print("Welcome!")                    // printed -- || also short-circuits
}

print(!enteredDoorCode)                  // false -- logical NOT

&& and || are short-circuiting: the right operand is evaluated only if the left one leaves the result undetermined, which means a right-hand side with a side effect (a function call) may not run at all — exactly like C, Java, or JavaScript.

Explicit Parentheses

let result = 2 + 3 % 4 * 5      // 17, following the precedence table below -- but is that clear at a glance?
let clearer = 2 + ((3 % 4) * 5) // also 17, spelled out

Swift lets every operator’s precedence resolve an unparenthesized expression unambiguously, but that does not make it readable — adding parentheses purely for clarity, even where they change nothing, is idiomatic Swift wherever a mix of operator kinds (bitwise with arithmetic, ?? with comparison, && with ||) could make a reader stop and think.

Precedence and Associativity

Every operator belongs to a precedence group, and each group has an explicit relative ordering to every other group plus its own associativity — the table below lists the standard library’s groups from highest to lowest:

Precedence group Representative operators Associativity

BitwiseShiftPrecedence

<< >>

none

MultiplicationPrecedence

/ % & &

left

AdditionPrecedence

` `-` `& &- | ^

left

RangeFormationPrecedence

…​ ..<

none

CastingPrecedence

is as as? as!

none

NilCoalescingPrecedence

??

right

ComparisonPrecedence

< > >= == != === !== ~=

none

LogicalConjunctionPrecedence

&&

left

LogicalDisjunctionPrecedence

||

left

DefaultPrecedence

most custom operators land here if unspecified

 — 

TernaryPrecedence

?:

right

AssignmentPrecedence

= += -= *= /=

right

infix operator +-: AdditionPrecedence
func +- (left: Int, right: Int) -> Int { left + right - right }
// declares a custom operator explicitly slotted into an existing precedence group

A group with associativity none cannot be chained without parentheses at all — 1 < 2 < 3 is a compile error, unlike a language where comparisons quietly chain or coerce to booleans. Declaring a custom operator’s own precedencegroup, with higherThan/lowerThan relations to existing groups, is covered alongside every other advanced-operator feature in Advanced Operators.

See Also