Bird
Raised Fist0
Swiftprogramming~10 mins

Type erasure concept in Swift - Step-by-Step Execution

Choose your learning style10 modes available
Concept Flow - Type erasure concept
Define Protocol with Associated Type
↓
Create Concrete Types conforming to Protocol
↓
Use Type Erasure Wrapper Struct
↓
Hide Concrete Types behind Wrapper
↓
Use Wrapper to store heterogeneous types uniformly
↓
Access Protocol Methods without exposing type details
Type erasure hides specific types behind a wrapper to use different concrete types uniformly through a protocol with associated types.
Execution Sample
Swift
protocol Shape {
  associatedtype Color
  func draw() -> String
}

struct Circle: Shape {
  typealias Color = Void
  func draw() -> String { "Circle" }
}

struct AnyShape: Shape {
  typealias Color = Void
  private let _draw: () -> String
  init<S: Shape>(_ shape: S) {
    _draw = shape.draw
  }
  func draw() -> String { _draw() }
}
This code defines a protocol with an associated type, a concrete type, and a type erasure wrapper to hide the concrete type.
Execution Table
StepActionEvaluationResult
1Define protocol Shape with associatedtype Color and draw()Protocol createdShape protocol ready
2Define struct Circle conforming to ShapeCircle implements draw()Circle ready
3Create AnyShape wrapper with closure _drawStore draw() of any ShapeAnyShape ready
4Initialize AnyShape with Circle instanceCapture Circle's draw()AnyShape stores Circle's draw()
5Call draw() on AnyShape instanceInvoke stored closure"Circle" string returned
6Use AnyShape to hold different Shape typesHide concrete typesUniform interface achieved
7ExitNo more stepsType erasure complete
💡 All steps executed to show how type erasure hides concrete types behind a uniform interface.
Variable Tracker
VariableStartAfter Step 4After Step 5Final
Circle instanceNot createdCreatedUsed in AnyShapeExists
AnyShape instanceNot createdCreated with CircleStores Circle's draw()Used to call draw()
_draw closureNot setSet to Circle.drawCalled to return "Circle"Holds Circle.draw
Key Moments - 3 Insights
Why do we need the AnyShape wrapper instead of using Circle directly?
Because protocols with associated types cannot be used as types directly, AnyShape hides the concrete type so we can store different Shapes uniformly (see execution_table step 4).
How does AnyShape call the draw() method of the wrapped type?
AnyShape stores a closure _draw that captures the draw() method of the concrete type, so calling draw() on AnyShape calls this stored closure (see execution_table step 5).
What does 'type erasure' mean in this context?
It means hiding the specific type details behind a wrapper so code can use different types through a common interface without knowing their exact types (see concept_flow).
Visual Quiz - 3 Questions
Test your understanding
Look at the execution_table at step 4, what does AnyShape store?
AThe concrete Circle instance directly
BA closure that calls Circle's draw() method
CThe protocol Shape itself
DNothing, it is empty
💡 Hint
Check the 'Evaluation' and 'Result' columns at step 4 in execution_table.
At which step does calling draw() on AnyShape return the string "Circle"?
AStep 5
BStep 4
CStep 3
DStep 6
💡 Hint
Look for when draw() is invoked and returns a value in execution_table.
If we add another Shape type, how would the variable _draw in AnyShape change?
AIt would store the old Circle's draw() method only
BIt would become nil
CIt would store a closure for the new type's draw() method
DIt would store the protocol Shape
💡 Hint
Refer to variable_tracker row for _draw closure and how it stores draw() methods.
Concept Snapshot
Type Erasure in Swift:
- Used to hide concrete types behind a protocol with associated types
- Wrap concrete types in a struct (e.g., AnyShape)
- Store protocol methods as closures inside wrapper
- Allows uniform use of different types
- Enables collections or variables of protocol type
- Key for working around Swift's protocol limitations
Full Transcript
Type erasure is a technique in Swift to hide the specific types of objects that conform to protocols with associated types. Because such protocols cannot be used directly as types, we create a wrapper struct like AnyShape. This wrapper stores the concrete type's methods as closures. When we call methods on the wrapper, it forwards the call to the stored closure, hiding the concrete type details. This allows us to use different concrete types uniformly through the wrapper. The execution steps show defining the protocol, creating a concrete type Circle, wrapping it in AnyShape, and calling draw() through the wrapper to get the expected output. Variables like the Circle instance and the closure _draw change as the program runs. Key moments clarify why the wrapper is needed and how it works. The quiz tests understanding of these steps and concepts.

Practice

(1/5)
1. What is the main purpose of type erasure in Swift?
easy
A. To convert classes into structs automatically
B. To create new types from existing ones
C. To improve runtime performance by avoiding protocols
D. To hide specific types behind a common interface

Solution

  1. Step 1: Understand type erasure concept

    Type erasure is used to hide the concrete underlying type and expose a common interface.
  2. Step 2: Identify the main purpose

    This allows different types to be used uniformly without exposing their specific details.
  3. Final Answer:

    To hide specific types behind a common interface -> Option D
  4. Quick Check:

    Type erasure = hiding types behind interface [OK]
Hint: Type erasure hides types to unify usage [OK]
Common Mistakes:
  • Confusing type erasure with type conversion
  • Thinking it improves performance directly
  • Assuming it creates new types
2. Which of the following is the correct syntax to create a type-erased wrapper for a protocol MyProtocol?
easy
A. struct AnyMyProtocol: MyProtocol { private let _base: any MyProtocol }
B. struct AnyMyProtocol<T>: MyProtocol { private let _base: T }
C. struct AnyMyProtocol: MyProtocol { private let _base: MyProtocol }
D. struct AnyMyProtocol: MyProtocol { private let _base: some MyProtocol }

Solution

  1. Step 1: Recall type erasure syntax

    Type erasure uses a struct that conforms to the protocol and stores an instance of any Protocol to hide the concrete type.
  2. Step 2: Identify correct syntax

    struct AnyMyProtocol: MyProtocol { private let _base: any MyProtocol } uses any MyProtocol which is the correct way to store an existential type in Swift 5.7+.
  3. Final Answer:

    struct AnyMyProtocol: MyProtocol { private let _base: any MyProtocol } -> Option A
  4. Quick Check:

    Use any Protocol for type erasure storage [OK]
Hint: Use 'any Protocol' to store erased type [OK]
Common Mistakes:
  • Using generic parameter instead of existential
  • Using 'some Protocol' which is opaque, not erased
  • Trying to store protocol directly without 'any'
3. Consider this Swift code using type erasure:
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?
medium
A. 28.566370614359172
B. 13.141592653589793
C. Error: Cannot convert Circle to AnyShape
D. 0

Solution

  1. Step 1: Calculate area of Circle and Square

    Circle area = π * 2 * 2 = 12.566370614359172, Square area = 4 * 4 = 16
  2. Step 2: Sum areas in array

    Total area = 12.566370614359172 + 16 = 28.566370614359172
  3. Step 3: Check code output

    Code prints totalArea, which is 28.566370614359172
  4. Final Answer:

    28.566370614359172 -> Option A
  5. Quick Check:

    Sum of areas = 28.566370614359172 [OK]
Hint: Add areas of shapes wrapped in AnyShape [OK]
Common Mistakes:
  • Calculating area incorrectly
  • Confusing type erasure with type casting
  • Expecting compile error due to protocol
4. This Swift code attempts type erasure but causes a compile error:
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?
medium
A. Missing conformance to Printer protocol
B. Cannot assign generic method to closure with Any parameter
C. Cannot use associatedtype in protocol
D. Function printItem has wrong return type

Solution

  1. Step 1: Analyze closure assignment

    The closure _print expects (Any) -> Void but printer.printItem expects (P.Item) -> Void, which is a mismatch.
  2. 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.
  3. Final Answer:

    Cannot assign generic method to closure with Any parameter -> Option B
  4. Quick Check:

    Generic method ≠ closure with Any parameter [OK]
Hint: Generic methods can't be assigned to Any closures directly [OK]
Common Mistakes:
  • Thinking protocol conformance is missing
  • Confusing associatedtype usage
  • Assuming return type mismatch
5. You want to create a type-erased wrapper for a protocol 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?
hard
A. Create a struct AnyDataSource with a private closure _fetch: () -> [Any] and cast results inside
B. Use a generic struct AnyDataSource<T> storing DataSource directly
C. Create a struct AnyDataSource with a private closure _fetch: () -> [Item] and use type erasure with a generic initializer
D. Use protocol inheritance to remove associated type

Solution

  1. Step 1: Understand associated type challenge

    Protocols with associated types cannot be used directly in collections because the type varies.
  2. 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.
  3. Step 3: Evaluate options

    Create a struct AnyDataSource with 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.
  4. Final Answer:

    Create a struct AnyDataSource with a private closure _fetch: () -> [Item] and use type erasure with a generic initializer -> Option C
  5. Quick Check:

    Type erasure with generic init and closure = Create a struct AnyDataSource with a private closure _fetch: () -> [Item] and use type erasure with a generic initializer [OK]
Hint: Use generic init and closure returning [Item] for erasure [OK]
Common Mistakes:
  • Casting to [Any] loses type safety
  • Using generic struct prevents uniform storage
  • Trying to remove associated type via inheritance
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard