Pattern Matching
Pattern matching compares values against patterns using switch
, if case
, for case
, and other constructs, enabling expressive and concise code for data decomposition and control flow.
Basic Patterns
- Value-Binding: Extract values into variables.
- Wildcard: Ignore values with
_
. - Literal: Match specific values.
Example: Basic Switch:
swift
let point = (x: 1, y: 0)
switch point {
case (0, 0):
print("Origin")
case let (x, _):
print("X: \(x)")
default:
print("Elsewhere")
}
Enum Patterns
Match enum cases, including associated values.
Example:
swift
enum Result {
case success(String)
case failure(Error)
}
let result = Result.success("Done")
switch result {
case let .success(message):
print("Success: \(message)")
case let .failure(error):
print("Error: \(error)")
}
Optional Patterns
Match optional values with .some
or .none
.
Example:
swift
let value: Int? = 42
if case .some(let x) = value {
print("Value: \(x)") // 42
}
if case .none = value {
print("No value")
}
Tuple Patterns
Decompose tuples into components.
Example:
swift
let status = (code: 200, message: "OK")
switch status {
case (200, let msg):
print("Success: \(msg)")
case (let code, _):
print("Code: \(code)")
}
Range Patterns
Match values within ranges.
Example:
swift
let score = 85
switch score {
case 90...100:
print("A")
case 80..<90:
print("B")
default:
print("C or below")
}
Expression Patterns
Match custom expressions with ~=
operator.
Example:
swift
struct StatusCode {
let value: Int
static func ~= (pattern: Range<Int>, code: StatusCode) -> Bool {
return pattern.contains(code.value)
}
}
let code = StatusCode(value: 404)
switch code {
case 200..<300:
print("Success")
case 400..<500:
print("Client Error") // "Client Error"
default:
print("Other")
}
Type-Casting Patterns
Use is
and as
for type checking and casting.
Example:
swift
let items: [Any] = [1, "Hello", 3.14]
for item in items {
switch item {
case is Int:
print("Integer")
case let str as String:
print("String: \(str)")
case let num as Double:
print("Double: \(num)")
default:
print("Unknown")
}
}
Where Clauses
Add conditions to patterns.
Example:
swift
switch point {
case let (x, y) where x == y:
print("On diagonal")
case let (x, y) where x > y:
print("Above diagonal")
default:
print("Below or elsewhere")
}
For Case Loops
Filter iterations with patterns.
Example:
swift
let results = [Result.success("A"), Result.failure(NSError()), Result.success("B")]
for case let .success(message) in results {
print("Success: \(message)") // "A", "B"
}
If Case Statements
Match patterns in conditional statements.
Example:
swift
if case let .success(message) = result {
print("Success: \(message)")
}
Nested Patterns
Combine patterns for complex matching.
Example:
swift
enum Event {
case userAction(userID: String, action: String)
case systemEvent(code: Int)
}
let event = Event.userAction(userID: "u1", action: "login")
switch event {
case let .userAction(id, action) where action == "login":
print("User \(id) logged in")
case .userAction(_, _):
print("Other user action")
case .systemEvent(let code):
print("System event: \(code)")
}
Best Practices
- Exhaustive Switches: Avoid
default
for enums to catch changes. - Clear Patterns: Use value-binding for readability.
- Where Clauses: Filter complex conditions.
- Type Casting: Prefer
as?
in patterns for safety. - Nested Patterns: Simplify with helper functions if complex.
- Test Patterns: Verify edge cases in unit tests.
Troubleshooting
- Non-Exhaustive Switch: Add missing cases or
default
. - Pattern Mismatch: Check pattern compatibility with value.
- Type Cast Failures: Use
is
to verify types. - Complex Patterns: Refactor into smaller switches.
- Performance: Optimize patterns for large datasets.
Example: Comprehensive Pattern Matching
swift
enum Transaction {
case payment(amount: Double, to: String)
case refund(amount: Double, reason: String)
case transfer(from: String, to: String, amount: Double)
}
let transactions: [Transaction] = [
.payment(amount: 50.0, to: "Alice"),
.refund(amount: 25.0, reason: "Defective"),
.transfer(from: "Bob", to: "Charlie", amount: 100.0)
]
for transaction in transactions {
switch transaction {
case let .payment(amount, to) where amount > 100:
print("Large payment of \(amount) to \(to)")
case .payment(let amount, let to):
print("Payment of \(amount) to \(to)")
case .refund(let amount, let reason):
print("Refund of \(amount) for \(reason)")
case .transfer(let from, let to, let amount) where from == to:
print("Invalid transfer of \(amount)")
case .transfer(let from, let to, let amount):
print("Transfer of \(amount) from \(from) to \(to)")
}
}