我正在尝试构建一个查找器,该查找器尝试找到在闭包内传递给它的多种类型。
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
}
我称取景器为:
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)
显然,带有四个参数的函数被编译器视为仅使一个通用参数“适合”声明。
我认为这是一个错误。我想我能猜出是什么原因造成的。它可能与函数参数列表作为元组的遗留问题有关。该函数
f
与采用包含四个字符串元组的单个参数的函数“等效”。但是,您实际上无法使用四字符串元组在function
中调用doSomething
;我根本找不到办法。因此,我想说的是,将其视为错误,并通过删除泛型的第一个版本暂时解决此问题。
更新:根据Swift团队的建议,我使用2020年5月4日的Swift 5.3开发工具链进行了测试。使用它,您的代码可以编译并按预期方式运行。这确实是一个错误,并且已作为以下部分修复
https://bugs.swift.org/browse/SR-8563
回到我的版本中,我的代码也可以按预期方式编译和运行,并且存在
doSomething
的所有四个版本。但是,请注意,如果您删除了除doSomething
的第一个版本以外的所有版本,它仍会编译并运行。此外,可以通过将四个参数捆绑到元组中并强制转换来调用带有四个参数的function
,如下所示:struct TestFinder2 {
func doSomething<T>(_ function: (T) -> Void) -> Void {
print("one")
function(("manny", "moe", "jack", "henry") as! T)
}
}
这似乎证实了我的猜测,即您所看到的是函数参数列表的隐藏元组性质的结果。从对该bug的讨论中可以得出相同的结论,即“tuple-splatting”。
关于ios - 重载iOS Swift中的通用函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61657382/