ProgramaciónDesarrollador iOS, Middle/Senior

¿Qué es la eliminación de tipo (type erasure) en Swift, para qué se necesita y cómo implementarlo correctamente en la práctica?

Supere entrevistas con el asistente de IA Hintsage

Respuesta.

Historia de la cuestión: La eliminación de tipo (type erasure) es un patrón que apareció en Swift como respuesta a las limitaciones de los protocolos con associated type o requisitos de Self. Un protocolo no se puede utilizar como un tipo directamente sin la eliminación de tipo, si contiene tipos asociados, ya que el compilador no conoce la implementación específica. Esto se encuentra a menudo al crear contenedores de datos, colecciones, flujos, donde la abstracción es importante.

Problema: Si hay un protocolo con associatedtype, por ejemplo:

protocol Animal { associatedtype Food func eat(_ food: Food) }

no se puede declarar una variable del tipo Animal. Necesitamos un tipo "que elimina" que permita referirse a diferentes tipos concretos a través de la abstracción:

let zoo: [any Animal] // Error de compilación

Solución: La eliminación de tipo se implementa utilizando envoltorios (por ejemplo, el patrón Box o struct AnyXxx), proporcionando una interfaz única, ocultando los detalles del tipo real:

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) } }

Ahora se pueden almacenar diferentes implementaciones de Animal con el mismo Food:

let animals: [AnyAnimal<Grass>] = [AnyAnimal(Cow()), AnyAnimal(Sheep())]

Características clave:

  • Permite trabajar con protocolos con associatedtype como si fueran tipos normales
  • Resuelve el problema de contenedores y fábricas de entidades de protocolos
  • Es un patrón arquitectónico para ocultar los detalles de la implementación

Preguntas engañosas.

¿Se puede utilizar un protocolo con associatedtype sin la eliminación de tipo como tipo de propiedad o matriz?

No, no se puede. El compilador requiere la especificación de todos los tipos asociados. Por ejemplo, la siguiente declaración provocará un error:

let array: [Animal] // Error: 'Animal' solo puede usarse como una restricción genérica

¿Puede la eliminación de tipo reemplazar la herencia?

No, la eliminación de tipo no es un reemplazo completo de la herencia. Resuelve el problema de la abstracción para protocolos con associatedtype, mientras que la herencia se utiliza para implementar lógica común y reutilizar código entre clases.

¿Es necesario implementar todos los métodos y propiedades del protocolo dentro de la envoltura de eliminación de tipo?

Sí, es obligatorio. La eliminación de tipo funciona solo si la envoltura repite completamente la interfaz externa del protocolo, de lo contrario, parte de la funcionalidad estará oculta.

Errores típicos y antipatrón

  • Olvidan implementar todos los métodos/propiedades, lo que lleva a una pérdida silenciosa de funcionalidad
  • Usan la eliminación de tipo donde se puede prescindir de ella (por ejemplo, si no hay associatedtype)
  • Crean envolturas redundantes o demasiado complejas, disminuyendo la legibilidad

Ejemplo de la vida real

Caso negativo

En un proyecto en producción, todo el trabajo con las fuentes de datos se basa en la eliminación de tipo, incluso cuando no hay necesidad de associatedtype. El código se vuelve difícil de mantener, los nuevos desarrolladores se confunden.

Ventajas:

  • Universalidad de la API

Desventajas:

  • Disminución de la legibilidad, complejidad de la arquitectura, errores latentes

Caso positivo

La eliminación de tipo se aplica solo para la abstracción de la fuente de datos, que encapsula diferentes estrategias de carga (redes, locales), cada una con sus propios tipos. En el resto, el código es transparente.

Ventajas:

  • Flexibilidad de la arquitectura, facilidad de integración de nuevas estrategias

Desventajas:

  • Añade una capa de intermediarios (wrappers), lo que complica un poco la depuración