opaque and boxes type
Opaque Types
Opaque types are used in Swift to hide the underlying type of a value, while still guaranteeing that the value conforms to certain protocols. When a function returns an opaque type, it's telling the compiler that it will return some type that conforms to a specific protocol, but it won't disclose what that type is. This way, the details of the type can remain private to the function or method.
The syntax to define a function that returns an opaque type uses the some keyword:
func makeIterator() -> some IteratorProtocol {
return [1, 2, 3, 4].makeIterator()
}
Here, the function makeIterator returns an object that conforms to IteratorProtocol, but the caller doesn't know the concrete type of the returned object. This can be useful for API design, where you want to keep implementation details hidden.
Benefits of Opaque Types
Type Privacy: Opaque types allow a function or method to hide the details of its internal workings.
Type Constraints: They provide a way to promise that a type conforms to multiple protocols without exposing the concrete underlying type.
Abstraction: They serve to abstract the type being returned, focusing on what the type can do (its protocol conformance) rather than what it is.
Box Types (Type Erasure)
While opaque types are a way to keep type information hidden but intact, type erasure essentially removes that type information in favor of another type that holds the erased value. This is sometimes necessary to store disparate types in a homogeneous collection. Type erasure is commonly implemented using a "box" or wrapper type.
Here is an example using Swift's Any type to perform type erasure:
let myInt: Int = 42
let myString: String = "hello"
let myAnyInt: Any = myInt
let myAnyString: Any = myString
let array: [Any] = [myAnyInt, myAnyString]
In this example, myAnyInt and myAnyString are boxed into Any types, erasing their original types and allowing them to be stored together in an array of Any.
Custom Type Erasure
Sometimes you'll create a custom wrapper to do more specific type erasure, especially if you want to retain some behavior or functionality of the erased types.
struct AnyAnimal: Animal {
private let _makeSound: () -> Void
init<A: Animal>(_ animal: A) {
self._makeSound = animal.makeSound
}
func makeSound() {
_makeSound()
}
}
Here, AnyAnimal acts as a box that can hold any type conforming to the Animal protocol, providing a way to use it later without knowing the type.
Summary
Opaque types preserve type identity, allowing you to hide implementation details while keeping the benefits of strong typing.
Box types, often created for type erasure, allow you to store values of different types as the same general type, but at the cost of losing type information.
Both concepts can be useful depending on the specific needs of your program.