Swift Language Requisiti del tipo associato


Esempio

I protocolli possono definire i requisiti di tipo associati utilizzando la parola chiave associatedtype :

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

I protocolli con i requisiti di tipo associati possono essere utilizzati solo come vincoli generici :

// 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 conforme al protocollo può soddisfare implicitamente un requisito di tipo associatedtype , fornendo un determinato tipo in cui il protocollo si aspetta che venga visualizzato il 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")

(Si noti che per aggiungere chiarezza a questo esempio, il tipo di segnaposto generico è denominato T - un nome più adatto sarebbe Element , che ombreggia l' associatedtype Element del protocollo. Il compilatore dedurrà comunque che l' Element segnaposto generico viene utilizzato per soddisfare il tipo associatedtype Element Requisito associatedtype Element .)

Un tipo associatedtype può anche essere soddisfatto esplicitamente attraverso l'uso di un typealias :

struct ContainerOfOne<T>: Container {

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

    // ...
}

Lo stesso vale per le estensioni:

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

Se il tipo conforme soddisfa già il requisito, non è necessaria alcuna implementazione:

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