2014-09-22 4 views
6

У меня есть, казалось бы, легитимная функция, которая преобразует массив в словарь:Swift не может вывести тип из контекста

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 }) 

это работает просто отлично. Зачем?

+0

'пусть ДИКТ = arrayToDictionary ([1, 2, 3], {вала в вале})' (вынул ' возврат') тоже похоже все работает нормально. –

+0

И даже незнакомец, явно определяющий 'val' как' Int' с 'return' на месте:' let dict = arrayToDictionary ([1, 2, 3], {(val: Int) в return val}) ' вызывает ошибку: «NSNumber» не является подтипом «Int''». Возвращение «возвращения» снова работает. –

+0

это похоже на ошибку компилятора, и я рекомендую вам сообщить об этом. Некоторая оптимизация происходит за кулисами, сделанными слишком рано, и проверка типа не радует этого. –

ответ

2

На этот вопрос может ответить только инженер-компилятор в Apple, и в отношении вышеупомянутых комментаторов он может/должен считаться ошибкой, но это определенно дыра в их сокращенном синтаксисе. Для таких вопросов я получил хорошие результаты от публикации в devforums.

Простым правилом является то, что всякий раз, когда у вас есть несколько строк/необходимо использовать ключевое слово return, вы должны либо явно определить тип возврата, либо тип измеряемого значения. Это ограничение может быть связано с тем, что в компактном/вырожденном случае у вас есть только одна точка выхода - val in val, где, как и при использовании ключевого слова return, возможно иметь несколько точек возврата. В этом последнем случае вы можете вернуть Int на одну строку return 1 и вернуть nil на другой. В этом случае было бы разумным, чтобы компилятор жаловался на то, чтобы сделать предположение явным. Короче говоря, это потребует более сложного вывода типа в компиляторе, и они, возможно, еще не достигли этого.

So TL; DR, я согласен с предложением сообщить об этом как об ошибке, и тем временем указать тип возврата из закрытия. Дело остается в том, что у компилятора достаточно контекста, чтобы вывести правильный тип, как вы сказали.

Обратите внимание, что в дополнении к примеру, эти случаи также работают:

// 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 }) 
Смежные вопросы