Skip to content

Instantly share code, notes, and snippets.

@bkobilansky
Forked from russbishop/TypeErasure.swift
Created September 9, 2016 00:58
Show Gist options
  • Save bkobilansky/8d8b8a36b6403d9ea6986f69b933b253 to your computer and use it in GitHub Desktop.
Save bkobilansky/8d8b8a36b6403d9ea6986f69b933b253 to your computer and use it in GitHub Desktop.
Type erasure with multiple adopting types
// Paste me into a playground!
import Cocoa
//: # Basic Setup
protocol FancyProtocol {
associatedtype Thing
func holdPinkyUp(x: Thing)
}
struct Dashing: FancyProtocol {
func holdPinkyUp(x: String) { print("Dashing: \(x)") }
}
struct Spiffy: FancyProtocol {
func holdPinkyUp(x: String) { print("Spiffy: \(x)") }
}
//: ## BoxBase
//: The base implements the protocol but everything just fatal errors.
//: It exists to give us an abstraction we can override later
class AnyFancyBoxBase<T>: FancyProtocol {
func holdPinkyUp(x: T) {
//never called
fatalError()
}
}
//: ## The Box
//: Here we override the BoxBase and specify that our generic parameter
//: implements the protocol. This is just a trampoline that forwards
//: everything to base.
final class _FancyBox<Base: FancyProtocol>: AnyFancyBoxBase<Base.Thing> {
var base: Base
init(_ base: Base) {
self.base = base
}
override func holdPinkyUp(x: Base.Thing) {
base.holdPinkyUp(x: x)
}
}
//: ## Type-erased wrapper
//: Our type-erased AnyFancy that specifies the base box class,
//: which being a base class frees us from knowing what is inside it.
//:
//: The generic initializer is using FancyProtocol as a generic constraint
//: and thus we satisfy the requirement of using an associated type
struct AnyFancy<T>: FancyProtocol {
var _box: AnyFancyBoxBase<T>
func holdPinkyUp(x: T) {
_box.holdPinkyUp(x: x)
}
init<U: FancyProtocol>(_ base: U) where U.Thing == T {
_box = _FancyBox(base)
}
}
let dashing = Dashing()
let spiffy = Spiffy()
//: ## Magic
//: Our type-erased AnyFancy that specifies the base box class,
//: which being a base class frees us from caring about the implementation
var anyFancy = AnyFancy(dashing)
print("\(type(of: anyFancy))")
anyFancy.holdPinkyUp(x: "ok")
//: Because Spiffy binds FancyProtocol.Thing to String it is compatible
anyFancy = AnyFancy(spiffy)
anyFancy.holdPinkyUp(x: "woo")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment