Swift Operator Overloading

Creating custom behavior for operators with your types

🔧 What is Operator Overloading?

Operator overloading allows you to define custom behavior for operators like +, -, *, == when used with your own types. This makes your code more intuitive and readable.


struct Point {
    var x: Double
    var y: Double
    
    static func + (left: Point, right: Point) -> Point {
        return Point(x: left.x + right.x, y: left.y + right.y)
    }
}

let point1 = Point(x: 1.0, y: 2.0)
let point2 = Point(x: 3.0, y: 4.0)
let sum = point1 + point2  // Point(x: 4.0, y: 6.0)
                                    

Operator Overloading Concepts

Arithmetic Operators

Overload +, -, *, / for custom math

static func + (left: Vector, right: Vector) -> Vector {
    return Vector(x: left.x + right.x)
}
⚖️

Comparison Operators

Define ==, !=, <, > for your types

static func == (left: Person, right: Person) -> Bool {
    return left.id == right.id
}
🔄

Unary Operators

Implement prefix and postfix operators

static prefix func - (point: Point) -> Point {
    return Point(x: -point.x, y: -point.y)
}
🎯

Custom Operators

Create entirely new operators

infix operator **: MultiplicationPrecedence
static func ** (base: Int, power: Int) -> Int {
    return Int(pow(Double(base), Double(power)))
}

🔹 Basic Arithmetic Overloading

Overload arithmetic operators for custom types:

struct Vector2D {
    var x: Double
    var y: Double
    
    // Addition operator
    static func + (left: Vector2D, right: Vector2D) -> Vector2D {
        return Vector2D(x: left.x + right.x, y: left.y + right.y)
    }
    
    // Subtraction operator
    static func - (left: Vector2D, right: Vector2D) -> Vector2D {
        return Vector2D(x: left.x - right.x, y: left.y - right.y)
    }
    
    // Multiplication by scalar
    static func * (vector: Vector2D, scalar: Double) -> Vector2D {
        return Vector2D(x: vector.x * scalar, y: vector.y * scalar)
    }
    
    // Division by scalar
    static func / (vector: Vector2D, scalar: Double) -> Vector2D {
        return Vector2D(x: vector.x / scalar, y: vector.y / scalar)
    }
}

// Usage examples
let vector1 = Vector2D(x: 3.0, y: 4.0)
let vector2 = Vector2D(x: 1.0, y: 2.0)

let sum = vector1 + vector2
let difference = vector1 - vector2
let scaled = vector1 * 2.0
let divided = vector1 / 2.0

print("Sum: (\(sum.x), \(sum.y))")
print("Difference: (\(difference.x), \(difference.y))")
print("Scaled: (\(scaled.x), \(scaled.y))")
print("Divided: (\(divided.x), \(divided.y))")

Output:

Sum: (4.0, 6.0)

Difference: (2.0, 2.0)

Scaled: (6.0, 8.0)

Divided: (1.5, 2.0)

🔹 Comparison Operators

Implement equality and comparison operators:

struct Student {
    let name: String
    let grade: Int
    let id: String
}

// Equatable conformance
extension Student: Equatable {
    static func == (left: Student, right: Student) -> Bool {
        return left.id == right.id
    }
}

// Comparable conformance
extension Student: Comparable {
    static func < (left: Student, right: Student) -> Bool {
        return left.grade < right.grade
    }
}

// Create students
let alice = Student(name: "Alice", grade: 85, id: "001")
let bob = Student(name: "Bob", grade: 92, id: "002")
let charlie = Student(name: "Charlie", grade: 85, id: "003")

// Test equality
print("Alice == Bob: \(alice == bob)")
print("Alice == Charlie: \(alice == charlie)")

// Test comparison
print("Alice < Bob: \(alice < bob)")
print("Bob > Charlie: \(bob > charlie)")

// Sort students by grade
let students = [alice, bob, charlie]
let sortedStudents = students.sorted()
print("Sorted by grade:")
for student in sortedStudents {
    print("\(student.name): \(student.grade)")
}

Output:

Alice == Bob: false

Alice == Charlie: false

Alice < Bob: true

Bob > Charlie: true

Sorted by grade:

Alice: 85

Charlie: 85

Bob: 92

🔹 Unary Operators

Implement prefix and postfix operators:

struct Counter {
    var value: Int
    
    // Prefix increment (++counter)
    static prefix func ++ (counter: inout Counter) -> Counter {
        counter.value += 1
        return counter
    }
    
    // Postfix increment (counter++)
    static postfix func ++ (counter: inout Counter) -> Counter {
        let oldValue = counter
        counter.value += 1
        return oldValue
    }
    
    // Prefix decrement (--counter)
    static prefix func -- (counter: inout Counter) -> Counter {
        counter.value -= 1
        return counter
    }
    
    // Unary minus (-counter)
    static prefix func - (counter: Counter) -> Counter {
        return Counter(value: -counter.value)
    }
    
    // Unary plus (+counter)
    static prefix func + (counter: Counter) -> Counter {
        return counter
    }
}

var counter = Counter(value: 5)
print("Initial: \(counter.value)")

let preIncrement = ++counter
print("After ++counter: \(counter.value), returned: \(preIncrement.value)")

let postIncrement = counter++
print("After counter++: \(counter.value), returned: \(postIncrement.value)")

let decremented = --counter
print("After --counter: \(counter.value)")

let negated = -counter
print("Negated: \(negated.value)")

let positive = +counter
print("Positive: \(positive.value)")

Output:

Initial: 5

After ++counter: 6, returned: 6

After counter++: 7, returned: 6

After --counter: 6

Negated: -6

Positive: 6

🔹 Custom Operators

Create entirely new operators for specific operations:

// Define a power operator
infix operator **: MultiplicationPrecedence

extension Int {
    static func ** (base: Int, power: Int) -> Int {
        return Int(pow(Double(base), Double(power)))
    }
}

// Define a dot product operator for vectors
infix operator •: MultiplicationPrecedence

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

// Define a range check operator
infix operator ~=: ComparisonPrecedence

extension Int {
    static func ~= (range: ClosedRange, value: Int) -> Bool {
        return range.contains(value)
    }
}

// Usage examples
let base = 2
let power = 3
let result = base ** power
print("\(base) ** \(power) = \(result)")

let vec1 = Vector2D(x: 3.0, y: 4.0)
let vec2 = Vector2D(x: 1.0, y: 2.0)
let dotProduct = vec1 • vec2
print("Dot product: \(dotProduct)")

let range = 1...10
let number = 5
let inRange = range ~= number
print("\(number) in range \(range): \(inRange)")

// String concatenation with custom operator
infix operator +++: AdditionPrecedence

extension String {
    static func +++ (left: String, right: String) -> String {
        return left + " " + right
    }
}

let greeting = "Hello" +++ "World"
print("Custom concatenation: \(greeting)")

Output:

2 ** 3 = 8

Dot product: 11.0

5 in range 1...10: true

Custom concatenation: Hello World

🔹 Compound Assignment Operators

Implement operators that modify the left operand:

struct Matrix2x2 {
    var a, b, c, d: Double
    
    init(_ a: Double, _ b: Double, _ c: Double, _ d: Double) {
        self.a = a; self.b = b; self.c = c; self.d = d
    }
    
    // Addition assignment
    static func += (left: inout Matrix2x2, right: Matrix2x2) {
        left.a += right.a
        left.b += right.b
        left.c += right.c
        left.d += right.d
    }
    
    // Scalar multiplication assignment
    static func *= (left: inout Matrix2x2, scalar: Double) {
        left.a *= scalar
        left.b *= scalar
        left.c *= scalar
        left.d *= scalar
    }
    
    // Regular addition for comparison
    static func + (left: Matrix2x2, right: Matrix2x2) -> Matrix2x2 {
        return Matrix2x2(left.a + right.a, left.b + right.b,
                        left.c + right.c, left.d + right.d)
    }
}

extension Matrix2x2: CustomStringConvertible {
    var description: String {
        return "[\(a), \(b)]\n[\(c), \(d)]"
    }
}

var matrix1 = Matrix2x2(1, 2, 3, 4)
let matrix2 = Matrix2x2(5, 6, 7, 8)

print("Matrix 1:")
print(matrix1)
print("\nMatrix 2:")
print(matrix2)

// Use compound assignment
matrix1 += matrix2
print("\nAfter matrix1 += matrix2:")
print(matrix1)

// Use scalar multiplication assignment
matrix1 *= 2.0
print("\nAfter matrix1 *= 2.0:")
print(matrix1)

Output:

Matrix 1:

[1.0, 2.0]

[3.0, 4.0]

Matrix 2:

[5.0, 6.0]

[7.0, 8.0]

After matrix1 += matrix2:

[6.0, 8.0]

[10.0, 12.0]

🧠 Test Your Knowledge

What keyword is used to define a custom infix operator?