Swift cannot infer type from context

I have a seemingly legitimate function that converts an array into 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 expression type '($ T6, (($ T9) → ($ T9) → $ T8) → (($ T9) → $ T8) → $ T8)' to print "Hashable"

Strange if I use implicit return:

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

or abbreviation:

 let dict = arrayToDictionary([1, 2, 3], { $0 }) 

It works great. Why?

+6
source share
1 answer

This question can only be answered by a compiler engineer at Apple, and for the aforementioned commentators it can / should be considered a mistake, but this is definitely a hole in their abbreviated syntax. For such questions, I got good results from posting in devforums.

A simple rule is that whenever you have multiple lines / you need to use the return keyword, you must either explicitly determine the type of return or the type of value captured. This restriction may be due to the fact that in the compact / degenerate case you are guaranteed only one exit point - val in val , where, as with the return keyword, it is possible to have several return points. In this last case, you can return Int to one line return 1 and return nil to another. In this case, it would be reasonable for the compiler to complain about making the assumption explicit. In short, this will require more complex type inference in the compiler, and they may not have achieved it yet.

So, TL, DR, I agree with the proposal to report this as an error, but for now indicate the type of return from closing. The point is 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 }) 
+2
source

Source: https://habr.com/ru/post/975671/


All Articles