Закрывающий кортеж не поддерживает деструктуризацию в Xcode 9 Swift 4
После глянцевого проекта для Swift 4 в Xcode 9
Я получаю следующую ошибку, о которой я понятия не имею
Параметр закрывающего кортежа '(ключ: _, значение: _) "не поддерживает деструктурирующий
Код:
extension Dictionary
{
init(elements: [Element]) {
self.init()
for (key, value) in elements {
self[key] = value
}
}
func flatMap<KeyPrime, ValuePrime>(_ transform: (Key, Value) throws -> (KeyPrime, ValuePrime)?) rethrows -> [KeyPrime:ValuePrime] {
return Dictionary<KeyPrime, ValuePrime>(elements: try flatMap({ (key, value) in
return try transform(key, value)
}))
}
}
На этом этапе возникает ошибка try flatMap({ (key, value)in
Ответы
Ответ 1
Начнем с определения flatMap
для словаря, который выглядит следующим образом:
func flatMap(_ transform: (Element) throws -> ElementOfResult?) rethrows -> [ElementOfResult]
Вы видите, что замыкание transform
принимает только один параметр типа Element
, где Element
является всего лишь typealias
для кортежа:
public typealias Element = (key: Key, value: Value)
Итак, первый аргумент и только закрытия должен быть кортежем из двух элементов (key
типа key
и value
типа value
).
Теперь, если вы посмотрите на свой код (который компилируется в Swift 3), вы увидите, что это не так, и вы должны спрашивать, почему это работает даже в Swift 3.
try flatMap({ (key, value) in
return try transform(key, value)
})
Ваше закрытие принимает 2 аргумента вместо одного (key
типа key
и value
типа value
). Это работает в Swift 3 благодаря функции, называемой деструктуризацией, где компилятор автоматически преобразует кортеж из 2 элементов в 2 аргумента.
Но эта функция является странной, редко используется и дает большие результаты в большинстве случаев, поэтому она была удалена в Swift 4.
Изменить. Как указано OOPer, эта функция была временно удалена в бета-версии Swift 4, но должна быть добавлена до окончательной версии.
Вместо этого вы должны писать:
try flatMap({ tupleArgument in
return try transform(tupleArgument.key, tupleArgument.value)
})
И ваша функция flatMap
будет выглядеть следующим образом:
func flatMap<KeyPrime, ValuePrime>(_ transform: (Key, Value) throws -> (KeyPrime, ValuePrime)?) rethrows -> [KeyPrime:ValuePrime] {
return Dictionary<KeyPrime, ValuePrime>(elements: try flatMap({ element in
return try transform(element.key, element.value)
}))
}
Ответ 2
Это побочный эффект этого предложения для Swift 4:
SE-0110 Различают типы функций с одним кортежем и несколькими аргументами.
Но некоторые функции, включенные в это предложение, вызвали некоторую регрессию, о которой говорится в этом сообщении списка рассылки эволюции-объявления:
[swift-evolution-announce] [Основная команда] Решение проблемы юзабилити SE-0110 в Swift 4
Таким образом, вы можете ожидать, что в будущей бета-версии или GM-версии Xcode 9 ваш код снова будет хорошо компилироваться. До этого вы можете использовать этот способ обхода:
internal func flatMap<KeyPrime , ValuePrime>(_ transform: (Key, Value) throws -> (KeyPrime, ValuePrime)?) rethrows -> [KeyPrime : ValuePrime] {
return Dictionary<KeyPrime,ValuePrime>(elements: try flatMap({ pair in
let (key, value) = pair
return try transform(key, value)
}))
}
Кстати, в Swift 4 в Dictionary
есть несколько новых инициализаторов, которые принимают Sequence
из пары (Key, Value)
. Например:
init (uniqueKeysWithValues: S)
Ответ 3
Я только что столкнулся с этой ошибкой в результате использования enumerated().map()
:
Параметр закрывающего кортежа не поддерживает деструктуризацию
Я набрал код:
["foo"].enumerated().map
А затем нажимали Enter 3 раза, пока XCode не завершит автозаполнение шаблона закрытия.
Автозаполнение, похоже, имеет ошибку, которая вызывает вышеуказанную ошибку. Автозаполнение приводит к двойным круглым скобкам ((offset: Int, element: String))
, а не к одиночным круглым (offset: Int, element: String)
.
Я исправил это вручную и смог продолжить:
// Xcode autocomplete suggests:
let fail = ["foo"].enumerated().map { ((offset: Int, element: String)) -> String in
return "ERROR: Closure tuple parameter does not support destructuring"
}
// Works if you manually replace the "(( _ ))" with "( _ )"
let pass = ["foo"].enumerated().map { (offset: Int, element: String) -> String in
return "works"
}
Возможно, результат использования бета-версии Xcode 10.0 (10L176w)