我想做一些事情沿着这条线:
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
时,我丢失了ArrayT
的Array
类型信息。
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]
)作为参数类型。