Operators
Swift’s operator surface is C-family at the core: arithmetic, comparison, logical, bitwise. The principal additions: range operators (..< half-open and ... closed), nil-coalescing (??), optional chaining (?.), force-unwrap (!), type-cast (as, as?, as!), range and pattern matching (~= for case/when), and overflow operators (&+, &-, &* for explicit wrap-around). Swift admits operator overloading — types may define +, -, ==, etc. — and custom operators (declared with the operator keyword). The combination — the conventional C-family operators, the optional-handling operators, the range operators, the overflow-explicit forms — is the substance of Swift’s expression surface.
Arithmetic
a + b // addition
a - b // subtraction
a * b // multiplication
a / b // division (integer for ints, float for floats)
a % b // remainder
-a // unary negation
+a // unary plus (rarely used)
Integer division truncates toward zero:
print(7 / 2) // 3
print(-7 / 2) // -3 (toward zero)
print(7 % 2) // 1
print(-7 % 2) // -1
For floating-point division, both operands must be float:
print(7.0 / 2.0) // 3.5
print(7 / 2) // 3 (Int)
print(Double(7) / Double(2)) // 3.5
There is no ** (power) operator in Swift; use pow(_:_:) from Foundation:
import Foundation
pow(2.0, 10.0) // 1024.0
For integer power, manual implementation or extensions are conventional.
Overflow behaviour
Swift traps on overflow by default:
let n = Int.max
let m = n + 1 // traps (runtime error)
For wrap-around, the overflow operators:
let n = Int.max
let m = n &+ 1 // Int.min (wraps)
// Also:
&-, &*
The default trapping eliminates substantial integer-overflow bugs.
Compound assignment
a += b // a = a + b
a -= b
a *= b
a /= b
a %= b
a &+= b // overflow add and assign
a &-= b
a &*= b
Comparison
a == b // equality
a != b
a < b
a > b
a <= b
a >= b
Equality (==) requires the type to conform to Equatable:
struct Point: Equatable {
let x: Int
let y: Int
}
let p1 = Point(x: 1, y: 2)
let p2 = Point(x: 1, y: 2)
print(p1 == p2) // true (synthesised)
The Equatable conformance is synthesised automatically for structs whose fields are all Equatable.
For ordering, the type must conform to Comparable:
struct Distance: Comparable {
let meters: Double
static func < (lhs: Distance, rhs: Distance) -> Bool {
lhs.meters < rhs.meters
}
}
[Distance(meters: 100), Distance(meters: 200)].sorted()
The < defines the ordering; Comparable derives <=, >, >=, ==.
Identity (===)
For reference types (classes), the === admits identity comparison (same object):
class Point {
var x: Int
init(x: Int) { self.x = x }
}
let p1 = Point(x: 1)
let p2 = Point(x: 1)
let p3 = p1
print(p1 === p2) // false (different objects)
print(p1 === p3) // true (same object)
print(p1 !== p2) // true
The == (when defined) is value equality; === is identity. Treated in Value and reference types.
Logical operators
a && b // AND (short-circuit)
a || b // OR (short-circuit)
!a // NOT
The operands must be Bool; Swift does not admit truthiness:
let n: Int = 5
if n { // ERROR: Int is not Bool
}
if n != 0 { // OK
}
The strictness eliminates the C-family truthiness pitfalls.
Bitwise operators
a & b // AND
a | b // OR
a ^ b // XOR
~a // NOT (unary)
a << b // left shift
a >> b // right shift (arithmetic for signed, logical for unsigned)
Swift’s right shift is arithmetic for signed integers (preserves sign) and logical for unsigned. For explicit logical shift on signed, the conversion idiom:
let n: Int8 = -1
let m = Int8(bitPattern: UInt8(bitPattern: n) >> 1)
Range operators
Two principal forms:
0..<10 // half-open: 0, 1, ..., 9
0...10 // closed: 0, 1, ..., 10
The half-open form is conventional for count-based iteration; the closed form for inclusive bounds:
for i in 0..<arr.count { // half-open admits all valid indices
process(arr[i])
}
let weekday = 1...7 // inclusive
One-sided ranges
let a = arr[0...] // from 0 to end
let b = arr[..<3] // up to (not including) 3
let c = arr[...] // entire array
The forms admit substantial slicing flexibility.
Optional operators
Optional chaining ?.
The ?. admits “access this if non-nil”:
let name: String? = "Alice"
let length = name?.count // length is Int?
let user: User? = nil
let city = user?.address?.city // String?
let result = list?.first?.uppercased() // String?
The form short-circuits on nil, returning nil for the entire expression.
Force unwrap !
The ! extracts the value or crashes if nil:
let name: String? = "Alice"
let length = name!.count // 5
let nilName: String? = nil
let oops = nilName!.count // CRASH: unexpectedly found nil
The conventional discipline avoids ! — if let, guard let, or nil-coalescing are conventionally safer.
Nil-coalescing ??
let name: String? = nil
let display = name ?? "anonymous" // "anonymous"
let port = config.port ?? 8080
The ?? admits “use the value, or this default if nil”.
For chained defaults:
let value = first ?? second ?? third ?? defaultValue
Type-cast operators
as (upcast)
let n: Int = 5
let any: Any = n as Any // upcast to Any (always succeeds)
as? (conditional cast)
let any: Any = "hello"
if let s = any as? String {
print(s) // "hello"
}
The as? returns Optional<T> — nil if the cast fails.
as! (force cast)
let any: Any = "hello"
let s = any as! String // crashes on failure
The ! form is conventional only when the cast is guaranteed to succeed (typically by surrounding logic).
Type-check operator is
let any: Any = "hello"
if any is String {
print("it's a string")
}
The is admits type-membership testing without binding.
Range and pattern operators
The ~= (range/pattern matching) admits switch/case matching:
let n = 5
switch n {
case 0:
print("zero")
case 1...10: // range pattern
print("small")
case _ where n.isMultiple(of: 5): // where guard
print("multiple of 5")
default:
print("other")
}
The ~= is overloaded for ranges, optionals, and types. Treated in Pattern matching.
Custom operators
Swift admits custom operators:
infix operator **: MultiplicationPrecedence
func ** (base: Double, exponent: Double) -> Double {
pow(base, exponent)
}
let result = 2.0 ** 10.0 // 1024.0
The form: declare the operator (prefix, infix, postfix) with optional precedence; define the function.
The conventional discipline reserves custom operators for substantial use — they admit substantial expressiveness but reduce readability.
Operator overloading
Existing operators may be overloaded:
struct Vector {
let x: Double
let y: Double
}
extension Vector {
static func + (a: Vector, b: Vector) -> Vector {
Vector(x: a.x + b.x, y: a.y + b.y)
}
static func * (v: Vector, scalar: Double) -> Vector {
Vector(x: v.x * scalar, y: v.y * scalar)
}
}
let v = Vector(x: 1, y: 2) + Vector(x: 3, y: 4)
The mechanism admits substantial mathematical syntax for user types.
Operator precedence
Swift’s principal precedence levels (high to low):
| Operators | Precedence |
|---|---|
<<, >> | BitwiseShift |
*, /, %, & | Multiplication |
+, -, |, ^ | Addition |
..<, ... | Range |
as, is | Casting |
?? | NilCoalescing |
<, <=, >, >=, ==, != | Comparison |
&& | LogicalConjunction |
|| | LogicalDisjunction |
?: | TernaryConditional |
=, +=, etc. | Assignment |
The complete precedence is more substantial; the conventional discipline uses parentheses freely.
Common patterns
Optional default
let name = user?.name ?? "anonymous"
let port = config.port ?? 8080
Optional chaining
let firstChar = name?.first?.uppercased()
let result = users.first?.profile?.address?.city
Range iteration
for i in 0..<10 {
process(i)
}
for c in "abc"..."xyz" {
/* admitted but rarely useful */
}
Conditional cast
if let user = sender as? User {
user.notify()
}
Tuple comparison
let a = (1, "alice")
let b = (1, "alice")
print(a == b) // true (lexicographic on tuple components)
Range slicing
let arr = [1, 2, 3, 4, 5]
let first3 = arr[0..<3] // ArraySlice<Int>
let last2 = arr[3...] // ArraySlice<Int>
let middle = arr[1..<4]
Pattern matching with ~=
let n = 5
if 1...10 ~= n {
print("in range")
}
The ~= admits explicit pattern matching outside switch.
Overflow check
let (sum, overflow) = a.addingReportingOverflow(b)
if overflow {
// handle
}
The methods on integer types admit explicit overflow detection without the trap.
A note on the absence of ++ and --
Swift removed ++ and -- (in Swift 3) — the conventional defence:
n += 1
n -= 1
The increment-as-expression form (e.g., arr[i++] in C) is rare in Swift’s iteration patterns; the explicit += is the conventional form.
A note on the conventional discipline
The contemporary Swift operator advice:
- Use
==for value equality;===for identity (classes). - Use
??over force-unwrap for default values. - Use
?.for optional chaining. - Use
!sparingly — only when the value is guaranteed non-nil. - Use
as?overas!for casts that may fail. - Use
..<for half-open ranges;...for closed. - Use
&+,&-,&*for explicit wrap-around. - Implement operators via extensions for clean conformance.
- Avoid custom operators unless substantively justified.
The combination — the conventional C-family operators, the optional-handling operators, the range and pattern operators, the overflow-trapping default with explicit wrap-around forms, the operator-overloading mechanism — is the substance of Swift’s expression surface. The discipline produces clear, type-safe expressions with substantial protection against the conventional integer and nil-pointer bugs.