Swift相关类型要求

示例

协议可以使用关键字定义关联的类型需求associatedtype:

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

具有相关类型要求的协议只能用作通用约束

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

// 这些是允许的:
func displayValues<T: Container>(container: T) { ... }
class MyClass<T: Container> { let container: T }

associatedtype通过在协议期望associatedtype出现的地方提供给定类型,符合协议的类型可以隐式满足要求:

struct ContainerOfOne<T>: Container {
    let count = 1          // 满足计数要求
    var value: T
    
    // 隐式满足下标关联类型要求,
    // 通过将下标分配/返回类型定义为T
    // 因此Swift会推断出T == Element
    subscript(index: Int) -> T {
        get {
            precondition(index == 0)
            return value
        }
        set {
            precondition(index == 0)
            value = newValue
        }
    }
}

let container = ContainerOfOne(value: "Hello")

(请注意,为使本示例更加清楚,将命名通用占位符类型T–更合适的名称为Element,它将掩盖协议的名称associatedtype Element。编译器仍会推断出通用占位符Element用于满足associatedtype Element要求。)

一个associatedtype也可以通过使用一个明确的满足typealias:

struct ContainerOfOne<T>: Container {    typealias Element = T
    subscript(index: Int) -> Element { ... }

    // ...
}

扩展也是如此:

// 公开一个8位整数作为布尔值的集合(每一位一个)。
extension UInt8: Container {

    // 如上所述,可以推断出这种类型别名
    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))
            }
        }
    }
}

如果符合类型已经满足要求,则不需要实现:

extension Array: Container {}  // 数组满足所有要求,包括元素