为什么这会引起这么多麻烦?(协议和类型的关联类型)



我想做一些事情沿着这条线:

protocol Protocol {
    associatedtype T
    associatedtype ArrayT = Array<T>
}
struct Struct<ProtocolType: Protocol> {
    func doSomething(with: ProtocolType.ArrayT) {
        let _ = with.map { $0 } 
        // ^ compiler complains on this line
        // "value of type ProtocolType.ArrayT has no member map"
    }
}   

我定义了一个使用associatedtype T的方便类型ArrayT。当我尝试像doSomething(_:)一样使用ArrayT时,我丢失了ArrayTArray类型信息。

ArrayT不应该是Array,因此是Sequence协议的成员,暴露map功能吗?🤔

我现在使用的工作解决方案是在协议之外定义一个泛型类型别名:

typealias ProtocolArray<ProtocolType: Protocol> = Array<ProtocolType.T>
struct Struct<ProtocolType: Protocol> {
    func doSomething(with: ProtocolArray<ProtocolType>) {
        let _ = with.map { $0 } // no complaints
    }
}

我在这里错过了什么?

associatedtype ArrayT = Array<T>行只告诉编译器ArrayT默认值Array<T>。协议的改编仍然可以改变ArrayT,如:

struct U: Protocol {
    typealias T = UInt32
    typealias ArrayT = UInt64   // <-- valid!
}

如果你想要一个固定的类型,你应该使用typealias…

// does not work yet.
protocol Protocol {
    associatedtype T
    typealias ArrayT = Array<T>
}

但是编译器抱怨类型太复杂🤷。所以你能做的最好的是约束 ArrayT是一个序列/集合等,并希望适配器不会改变自己的类型。

// still somewhat off
protocol Protocol {
    associatedtype T
    associatedtype ArrayT: Sequence = [T]
}

注意,Sequence可以有任何元素类型,但是我们希望ArrayT的element必须是t。不能在associatedtype上附加where子句:

// fail to compile: 'where' clause cannot be attached to an associated type declaration
associatedtype ArrayT: Sequence where Iterator.Element == T = [T]
相反,您需要在每次使用协议时设置此约束:
struct Struct<ProtocolType: Protocol> 
    where ProtocolType.ArrayT.Iterator.Element == ProtocolType.T {

完整的工作代码:

protocol Protocol {
    associatedtype T
    associatedtype ArrayT: Sequence = [T]
//                       ^~~~~~~~~~
}
struct Struct<ProtocolType: Protocol> 
    where ProtocolType.ArrayT.Iterator.Element == ProtocolType.T 
//  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
{
    func doSomething(w: ProtocolType.ArrayT) {
        let _: [ProtocolType.T] = w.map { $0 }
    }
}

当您"初始化" associatedtype时,您并没有定义它。这并不是关联类型的意义所在。关联的类型被故意解除绑定("占位符"),直到采用的类将它们全部解析。你在这里所做的就是给它一个默认值,一个符合标准的类可以重写它。扩展您的示例:

protocol Protocol {
    associatedtype T
    associatedtype ArrayT = Array<Self.T>
    func useSomeT(myFavoriteT: T)
    func useSomeArrayT(myLeastFavoriteArrayT: ArrayT)
}
class Whatever: Protocol {
    func useSomeT(myFavoriteT: Int) {
        print("My Favorite Int: (myFavoriteT)")
    }
    func useSomeArrayT(myLeastFavoriteArrayT: [Int: String]) {
        print(myLeastFavoriteArrayT.map { $0.1 })
    }
}
struct Struct<ProtocolType: Protocol> {
    func doSomething(stuff: ProtocolType.ArrayT) {
        print(type(of: stuff))
    }
}
let x = Struct<Whatever>()
x.doSomething(stuff: [3: "Doggies"])

对于您的示例,似乎您真正想要的是声明with: Array<ProtocolType.T>(或简单地with: [ProtocolType.T])作为参数类型。

最新更新