I have a seemingly legitimate function that converts an array to a Dictionary:
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
}
Now, when I try to call it:
let dict = arrayToDictionary([1, 2, 3], { val in return val })
I get an error - Cannot convert the expression's type '($T6, (($T9) -> ($T9) -> $T8) -> (($T9) -> $T8) -> $T8)' to type 'Hashable'
Strangely, if i use implicit return:
let dict = arrayToDictionary([1, 2, 3], { val in val })
or shorthand:
let dict = arrayToDictionary([1, 2, 3], { $0 })
it works just fine. Why?
This question can only really be answered by a compiler engineer at Apple, and per the above commenters it could / should be considered a bug, but it is definitely a hole in their shorthand syntax. For questions like these, I have gotten good results from posting to devforums.
The simple rule though is that whenever you have multiple lines / need to use the return
keyword, you must either explicitly define the return type, or the type of the value being captured. This limitation might be due to the fact that in the compact / degenerate case, you are guaranteed to only have one point of exit - val in val
, where as when you use a return
keyword, it is possible to have multiple return points. In this latter case, you might return an Int on one line return 1
, and return nil
on another. In that case, it would be reasonable to have the compiler complain to make the assumption explicit. In short, this would require more sophisticated type inference in the compiler, and they might not have gotten to that yet.
So TL;DR, I agree with the suggestion to report this as a bug, and in the meantime, specify a return type from the closure. The point remains though that the compiler has enough context to infer the correct type, as you stated.
Note, that in addition to your example, these cases also work:
// 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 })
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With