Swift无法从上下文推断类型

7

我有一个看似合法的函数,可以将数组转换为字典:

func arrayToDictionary<Key : Hashable, Value>
  (source: Array<Value>, key: Value -> Key) -> Dictionary<Key, Value> {
   var dictionary = Dictionary<Key, Value>()
   for element in source {
     let key = key(element)
     dictionary[key] = element
   }
   return dictionary
}

现在,当我尝试调用它时:
let dict = arrayToDictionary([1, 2, 3], { val in return val })

我遇到了一个错误 - 无法将表达式的类型“($T6,(($T9) -> ($T9) -> $T8) -> (($T9) -> $T8) -> $T8)”转换为类型“Hashable”。

奇怪的是,如果我使用隐式返回:

let dict = arrayToDictionary([1, 2, 3], { val in val })

或者简写为:
let dict = arrayToDictionary([1, 2, 3], { $0 })

它表现得非常良好。为什么呢?


让字典等于arrayToDictionary([1, 2, 3], { val in val })(去掉了return)似乎也可以正常工作。 - Mike S
而更奇怪的是,使用return明确将val 定义为 Int 类型的代码:let dict = arrayToDictionary([1, 2, 3], { (val: Int) in return val })会导致错误:'NSNumber' is not a subtype of 'Int'。如果再次移除 return 则代码正常运行。 - Mike S
这对我来说看起来像是编译器的一个错误,我建议你报告它。一些优化在幕后进行得太早了,类型检查器对此不满意。 - Jochen Bedersdorfer
Swift在块中的类型推断方面存在一些严重问题。有人告诉我,由于Swift中的类型信息严格向前流动,而不是具有适当的推断,因此它可能会使一些通用表达式统一到非常奇怪的类型。 - CodaFi
1个回答

5
这个问题只有在苹果公司的编译器工程师回答时才能得到真正的解答,而根据上述评论者的说法,这可能应该被视为一个错误,但它确实是他们简写语法中的漏洞。对于这类问题,我在devforums上发布了好的结果。
简单的规则是,每当你有多行/需要使用返回关键字时,你必须明确定义返回类型或所捕获的值的类型。这种限制可能是由于在紧凑/退化情况下,你保证只有一个退出点 - val in val,而在使用返回关键字时,可能有多个返回点。在后一种情况下,你可能会在一行上返回 Int return 1,并在另一行上返回 nil。在这种情况下,让编译器抱怨以使假设明确是合理的。简而言之,这将需要编译器中更复杂的类型推断,而他们可能尚未实现此功能。
因此,简要来说,我同意报告此问题作为一个错误,并在此期间指定闭包的返回类型。重点仍然是编译器具有足够的上下文来推断正确的类型,正如你所说。
请注意,除了你的示例之外,这些情况也可以运行:
// inferring var dict as a context for lines below
var dict = arrayToDictionary([1, 2, 3], { val in val })

// dict is already defined, so this works:
dict = arrayToDictionary([1, 2, 3], { val in return val })

// explicit final type, compiler infers backwards
let d2:Dictionary<Int, Int> = arrayToDictionary([1, 2, 3], { val in val })

// explicit return type, compiler infers "forewards"
let d3 = arrayToDictionary([1, 2, 3], { val -> Int in return val })

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接