Swift Language Requisitos de tipo asociado


Ejemplo

Los protocolos pueden definir los requisitos de tipo asociados utilizando la palabra clave de tipo associatedtype :

protocol Container {
    associatedtype Element
    var count: Int { get }
    subscript(index: Int) -> Element { get set }
}

Los protocolos con requisitos de tipo asociados solo pueden usarse como restricciones genéricas :

// These are NOT allowed, because Container has associated type requirements:
func displayValues(container: Container) { ... }
class MyClass { let container: Container }
// > error: protocol 'Container' can only be used as a generic constraint
// > because it has Self or associated type requirements

// These are allowed:
func displayValues<T: Container>(container: T) { ... }
class MyClass<T: Container> { let container: T }

Un tipo que se ajusta al protocolo puede satisfacer un requisito de tipo associatedtype implícitamente, al proporcionar un tipo dado en el que el protocolo espera que aparezca el tipo associatedtype :

struct ContainerOfOne<T>: Container {
    let count = 1          // satisfy the count requirement
    var value: T
    
    // satisfy the subscript associatedtype requirement implicitly,
    // by defining the subscript assignment/return type as T
    // therefore Swift will infer that T == Element
    subscript(index: Int) -> T {
        get {
            precondition(index == 0)
            return value
        }
        set {
            precondition(index == 0)
            value = newValue
        }
    }
}

let container = ContainerOfOne(value: "Hello")

(Tenga en cuenta que para agregar claridad a este ejemplo, el tipo de marcador de posición genérico se denomina T ; un nombre más adecuado sería Element , lo que ocultaría el associatedtype Element del protocolo. El compilador todavía inferirá que el Element marcador de posición genérico se utiliza para satisfacer el tipo associatedtype Element Requisito del associatedtype Element .)

Un tipo associatedtype también puede satisfacerse explícitamente mediante el uso de typealias :

struct ContainerOfOne<T>: Container {

    typealias Element = T
    subscript(index: Int) -> Element { ... }

    // ...
}

Lo mismo ocurre con las extensiones:

// Expose an 8-bit integer as a collection of boolean values (one for each bit).
extension UInt8: Container {

    // as noted above, this typealias can be inferred
    typealias Element = Bool

    var count: Int { return 8 }
    subscript(index: Int) -> Bool {
        get {
            precondition(0 <= index && index < 8)
            return self & 1 << UInt8(index) != 0
        }
        set {
            precondition(0 <= index && index < 8)
            if newValue {
                self |= 1 << UInt8(index)
            } else {
                self &= ~(1 << UInt8(index))
            }
        }
    }
}

Si el tipo conforme ya cumple con el requisito, no se necesita implementación:

extension Array: Container {}  // Array satisfies all requirements, including Element