Historia pytania: Type erasure (wymazanie typu) to wzorzec, który pojawił się w Swift jako odpowiedź na ograniczenia protokołów z associated type lub wymogami Self. Protokół nie może być używany jako typ bezpośrednio bez type erasure, jeśli zawiera powiązane typy, ponieważ kompilator nie zna konkretnej implementacji. Z tym często spotykają się podczas tworzenia kontenerów danych, kolekcji, strumieni, w których ważna jest abstrakcja.
Problem: Jeśli istnieje protokół z associatedtype, na przykład:
protocol Animal { associatedtype Food func eat(_ food: Food) }
nie można zadeklarować zmiennej typu Animal. Potrzebujemy "typ wymazujący", który umożliwia odwoływanie się do różnych konkretnych typów przez abstrakcję:
let zoo: [any Animal] // Błąd kompilacji
Rozwiązanie: Type erasure jest realizowane za pomocą opakowań (na przykład, wzorzec Box lub struct AnyXxx), dostarczając jednolity interfejs, ukrywając szczegóły rzeczywistego typu:
struct AnyAnimal<F>: Animal { private let _eat: (F) -> Void init<A: Animal>(_ base: A) where A.Food == F { _eat = base.eat } func eat(_ food: F) { _eat(food) } }
Teraz można przechowywać różne implementacje Animal z tym samym Food:
let animals: [AnyAnimal<Grass>] = [AnyAnimal(Cow()), AnyAnimal(Sheep())]
Kluczowe cechy:
Czy można używać protokołu z associatedtype bez type erasure jako typ właściwości lub tablicy?
Nie, nie można. Kompilator wymaga konkretizacji wszystkich powiązanych typów. Na przykład poniższy zapis spowoduje błąd:
let array: [Animal] // Błąd: 'Animal' może być używane tylko jako ograniczenie generyczne
Czy type erasure może zastąpić dziedziczenie?
Nie, type erasure nie jest pełnym zastępstwem dla dziedziczenia. Rozwiązuje problem abstrakcji dla protokołów z associatedtype, podczas gdy dziedziczenie jest używane do realizacji wspólnej logiki i ponownego użycia kodu między klasami.
Czy należy implementować wszystkie metody i właściwości protokołu wewnątrz powłoki type erasure?
Tak, koniecznie. Type erasure działa tylko, jeśli opakowanie w pełni powtarza zewnętrzny interfejs protokołu, w przeciwnym razie część funkcjonalności będzie niedostępna.
W projekcie produkcyjnym cała praca z źródłami danych jest oparta na type erasure, nawet gdy nie ma potrzeby w associatedtype. Kod staje się trudny do utrzymania, nowi programiści są zdezorientowani.
Zalety:
Wady:
Type erasure jest stosowane tylko do abstrakcji źródła danych, które inkapsuluje różne strategie ładowania (sieciowe, lokalne), każda ze swoimi typami. W przeciwnym razie kod jest przezroczysty.
Zalety:
Wady: