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]