我试图建立一个finder,它可以尝试并找到在一个闭包中传递给它的多个类型。
enum SomeError: Error {
case notInitialized
}
struct TestFinder {
func getSomething<T, U>(_ function: @escaping (T) -> U) throws -> U {
guard let t: T = get() else {
throw SomeError.notInitialized
}
return function(t)
}
func getSomething<T, U, V>(_ function: @escaping (T, U) -> V) throws -> V {
guard let t: T = get(), let u: U = get() else {
throw SomeError.notInitialized
}
return function(t, u)
}
func getSomething<T, U, V, W>(_ function: @escaping (T, U, V) -> W) throws -> W {
guard let t: T = get(), let u: U = get(), let v: V = get() else {
throw SomeError.notInitialized
}
return function(t, u, v)
}
private func get<T>() -> T? {
nil
}
}
struct UserDetails {
let name: String
let roll: String
}
我将finder调用为。
let testReturnType = try? TestFinder().getSomething(UserDetails.init)
编译器向我抛出一个错误:
"getSomething "的使用有误。
这个错误的原因(来自文档)。
你可以通过在类型参数上提供不同的约束、要求或两者都提供来重载一个通用函数或初始化器。当你调用一个重载的泛型函数或初始化器时,编译器会使用这些约束来解决要调用哪个重载函数或初始化器。
但如果我注释。
func getSomething<T, U>(_ function: @escaping (T) -> U) throws -> U
一切都开始工作了 这与编译器无法识别解析哪个函数签名有关。
有什么特别的解决方法吗?
你还没有完全集中在实际问题上。让我们从这个例子中删除所有不相关的东西。这样编译后就能正常工作了。
struct TestFinder {
func doSomething<T,U>(_ function: (T,U) -> Void) -> Void {
print("two")
}
func doSomething<T,U,V>(_ function: (T,U,V) -> Void) -> Void {
print("three")
}
func doSomething<T,U,V,W>(_ function: (T,U,V,W) -> Void) -> Void {
print("four")
}
}
我们在这里测试一下
func f(_ s1: String, _ s2: String, _ s3: String, _ s4: String) -> Void {}
TestFinder().doSomething(f) // "four"
但如果你在版本中加入 一个 传递的函数参数,一切都崩溃了。
struct TestFinder {
func doSomething<T>(_ function: (T) -> Void) -> Void {
print("one")
}
func doSomething<T,U>(_ function: (T,U) -> Void) -> Void {
print("two")
}
func doSomething<T,U,V>(_ function: (T,U,V) -> Void) -> Void {
print("three")
}
func doSomething<T,U,V,W>(_ function: (T,U,V,W) -> Void) -> Void {
print("four")
}
}
现在我们不能编译了,因为... 第一 版本被视为候选人。而事实上,如果我们把 其他 版本,我们 还 编译!
struct TestFinder {
func doSomething<T>(_ function: (T) -> Void) -> Void {
print("one")
}
}
这就是奇怪的地方。我们仍然在编译,即使我们在说。
func f(_ s1: String, _ s2: String, _ s3: String, _ s4: String) -> Void {}
TestFinder().doSomething(f)
很明显,这个有四个参数的函数 被编译器认为是 "适合 "声明中的 "适合"。一个 通用参数。
我认为这是一个bug。我想我可以猜到是什么原因造成的,可能与函数参数列表为元组的传统有关。这个函数 f
是 "等同于 "一个函数取一个 单一 参数由一个四串元组组成。然而,你实际上不能 召唤 的 function
里面 doSomething
的四串元组;我根本找不到调用它的方法。
所以,我想说的是,把这看作是一个bug,暂时通过移除 第一 版本的通用。
更新。 在Swift团队的建议下,我用2020年5月4日的Swift 5.3开发工具链进行了测试。使用它,你的代码编译和行为与预期一致。这确实是一个bug,并且它已经被修复为下列问题的一部分
https:/bugs.swift.orgbrowseSR-8563。
先回到我的版本,我的代码也是如此,编译和行为都符合预期,四个版本的 doSomething
的存在。但是,请注意,如果您删除了除第一个版本以外的所有的 doSomething
它仍然可以编译和运行。此外,你可以调用 function
通过将四个参数捆绑成一个元组并强制铸造,就像这样。
struct TestFinder2 {
func doSomething<T>(_ function: (T) -> Void) -> Void {
print("one")
function(("manny", "moe", "jack", "henry") as! T)
}
}
这似乎证实了我的猜测 你所看到的是一个函数参数列表中隐藏的元组性质的结果。我们可以从关于这个bug的讨论中得出同样的结论,其中提到了 "元组-splatting"。
关键点是UserDetails结构,因为这个结构有两个属性,而且没有任何的 设计的初始化器,初始化器可以是UserDetails(name:,roll:)或UserDetails(name:)或UserDetails(roll:),这是雄心勃勃的部分,如果你只是删除UserDetails的一个属性也可以,因为一个属性结构只有一个设计的初始化器。
如果你评论
func getSomething<T, U>(_ function: @escaping (T) -> U) throws -> U
也就是说,寻人者只有一个选择。
func getSomething<T,U,V>(_ function: @escaping(T,U) -> V) throws -> V