我有一组从字典中提取值的函数,其中字典的值类型为Any
(它来自JSON数据,我无法控制值的类型):
func get<T>(_ dict: [String: Any], key: String) throws -> T {
// cast the value for the key to a T, and return
}
func get<T: RawRepresentable>(_ dict: [String: Any], key: String) throws -> T {
// cast the value for the key to a T.RawValue, init a T, and return
}
这很好。第一个函数可以转换标量类型的值并将其返回,而第二个函数可以将enum
类型的原始值转换为更高级别的Swift类型。
如果添加第三个变体,则编译器会感到不满意,并且会得到很多“模棱两可”的错误,将所有三个功能标记为候选:
func get<T: RawRepresentable>(_ dict: [String: Any], key: String) throws -> T where T.RawValue == Double {
// try to extract the value as an Double, init a T, and return
// if that fails, extract as an Int, cast to a Double, init a T, and return
}
使用此功能的想法是,双精度值可能不带小数出现,并且实际上是字典中的Int
,但在强类型的Swift-land中它必须是Double
为什么编译器能够在没有第三种变体的情况下解决歧义,却将所有这三种变量都视为同等有效的候选者?
编辑:这是一个说明问题的操场。
enum E: Error {
case error
}
func get<T>(_ dict: [String: Any], key: String) throws -> T {
guard let value = dict[key] as? T else { throw E.error }
return value
}
func get<T: RawRepresentable>(_ dict: [String: Any], key: String) throws -> T {
guard let rawValue = dict[key] as? T.RawValue else { throw E.error }
return T(rawValue: rawValue)!
}
func get<T: RawRepresentable>(_ dict: [String: Any], key: String) throws -> T where T.RawValue == Double {
if let doubleValue = dict[key] as? Double {
return T(rawValue: doubleValue)!
} else if let intValue = dict[key] as? Int {
return T(rawValue: Double(intValue))!
}
throw E.error
}
struct Wrapped: RawRepresentable {
typealias RawValue = Double
var rawValue: Double
init?(rawValue: Double) {
self.rawValue = rawValue
}
}
let dict: [String: Any] = ["int": 1, "double": 1.1]
let int: Int = try! get(dict, key: "int")
let double: Double = try! get(dict, key: "double")
let wrapped: Wrapped = try! get(dict, key: "int")
这会产生编译错误(模糊使用get(_:key:)
)。注释掉get<T>
的第三种专业化可解决编译器错误,但会导致运行时错误,因为无法使用int
将键as?
的整数值转换为{{1 }}。将密钥更改为Double
可以解决此问题,但不能解决我试图解决的问题。
我应该注意,这对我来说是一个学术问题,因为我已经更改了解析方法以使用double
,并且它可以将浮点和整数值解析为Codable
/ {{ 1}}类型的Swift。