What if you could treat many different things as one without losing what makes each special?
Why Type erasure concept in Swift? - Purpose & Use Cases
Imagine you have different kinds of boxes, each holding a different type of toy. You want to put all these boxes on one shelf, but the shelf only accepts boxes of the same type. You try to manually wrap each box to look the same, but it quickly becomes confusing and messy.
Manually handling different types means writing lots of repetitive code to treat each type separately. It's slow, error-prone, and hard to maintain. You lose flexibility because you can't easily mix different types in one place without complex workarounds.
Type erasure acts like a universal wrapper that hides the specific type inside, making all boxes look the same to the shelf. This lets you store and use different types together seamlessly, without losing the unique behavior of each type.
func printAll<T>(items: [T]) { for item in items { print(item) } } // But T must be same typestruct AnyBox { private let _print: () -> Void; init<T>(_ box: T) { _print = { print(box) } } func printBox() { _print() } }It enables writing flexible, reusable code that can handle many different types uniformly without losing their unique features.
In SwiftUI development, you might want to store different views in one list. Type erasure lets you wrap each view so they can all be treated as the same type, making your UI code simpler and cleaner.
Manual handling of multiple types is complex and error-prone.
Type erasure hides specific types behind a common interface.
This allows flexible and clean code that works with many types together.
Practice
type erasure in Swift?Solution
Step 1: Understand type erasure concept
Type erasure is used to hide the concrete underlying type and expose a common interface.Step 2: Identify the main purpose
This allows different types to be used uniformly without exposing their specific details.Final Answer:
To hide specific types behind a common interface -> Option DQuick Check:
Type erasure = hiding types behind interface [OK]
- Confusing type erasure with type conversion
- Thinking it improves performance directly
- Assuming it creates new types
MyProtocol?Solution
Step 1: Recall type erasure syntax
Type erasure uses a struct that conforms to the protocol and stores an instance ofany Protocolto hide the concrete type.Step 2: Identify correct syntax
struct AnyMyProtocol: MyProtocol { private let _base: any MyProtocol } usesany MyProtocolwhich is the correct way to store an existential type in Swift 5.7+.Final Answer:
struct AnyMyProtocol: MyProtocol { private let _base: any MyProtocol } -> Option AQuick Check:
Useany Protocolfor type erasure storage [OK]
- Using generic parameter instead of existential
- Using 'some Protocol' which is opaque, not erased
- Trying to store protocol directly without 'any'
protocol Shape {
func area() -> Double
}
struct Circle: Shape {
var radius: Double
func area() -> Double { return .pi * radius * radius }
}
struct Square: Shape {
var side: Double
func area() -> Double { return side * side }
}
struct AnyShape: Shape {
private let _area: () -> Double
init(_ shape: S) {
_area = shape.area
}
func area() -> Double { _area() }
}
let shapes: [AnyShape] = [AnyShape(Circle(radius: 2)), AnyShape(Square(side: 4))]
let totalArea = shapes.reduce(0) { $0 + $1.area() }
print(totalArea)
What is the output?Solution
Step 1: Calculate area of Circle and Square
Circle area = π * 2 * 2 = 12.566370614359172, Square area = 4 * 4 = 16Step 2: Sum areas in array
Total area = 12.566370614359172 + 16 = 28.566370614359172Step 3: Check code output
Code prints totalArea, which is 28.566370614359172Final Answer:
28.566370614359172 -> Option AQuick Check:
Sum of areas = 28.566370614359172 [OK]
- Calculating area incorrectly
- Confusing type erasure with type casting
- Expecting compile error due to protocol
protocol Printer {
associatedtype Item
func printItem(_ item: Item)
}
struct AnyPrinter: Printer {
private let _print: (Any) -> Void
init(_ printer: P) {
_print = printer.printItem
}
func printItem(_ item: Any) {
_print(item)
}
}
What is the main cause of the error?Solution
Step 1: Analyze closure assignment
The closure _print expects (Any) -> Void but printer.printItem expects (P.Item) -> Void, which is a mismatch.Step 2: Understand type erasure limitation
Associated types prevent direct assignment because the types differ; you cannot assign a generic method to a closure with Any parameter.Final Answer:
Cannot assign generic method to closure with Any parameter -> Option BQuick Check:
Generic method ≠ closure with Any parameter [OK]
- Thinking protocol conformance is missing
- Confusing associatedtype usage
- Assuming return type mismatch
DataSource with an associated type Item and a method func fetch() -> [Item]. Which approach correctly applies type erasure to allow storing different DataSource implementations in a single array?Solution
Step 1: Understand associated type challenge
Protocols with associated types cannot be used directly in collections because the type varies.Step 2: Apply type erasure pattern
Create a struct with a private closure returning [Item], and initialize it generically to wrap any DataSource, hiding the concrete type.Step 3: Evaluate options
Create a structAnyDataSourcewith a private closure_fetch: () -> [Item]and use type erasure with a generic initializer correctly uses a generic initializer and closure with [Item], preserving type safety and enabling storage in arrays.Final Answer:
Create a struct AnyDataSource with a private closure _fetch: () -> [Item] and use type erasure with a generic initializer -> Option CQuick Check:
Type erasure with generic init and closure = Create a structAnyDataSourcewith a private closure_fetch: () -> [Item]and use type erasure with a generic initializer [OK]
- Casting to [Any] loses type safety
- Using generic struct prevents uniform storage
- Trying to remove associated type via inheritance
Ready to prove it?
Pass the test and this pattern moves unknown
