Кортеж "upcasting" в Swift
Если у меня есть кортеж с подписью (String, Bool)
Я не могу бросить это (String, Any)
, Компилятор говорит:
ошибка: невозможно выразить преобразование кортежа '(String, Bool)' в '(String, Any)'
Но это должно работать, так как Bool
может быть безопасно приведен к Any
с as
, Почти такая же ошибка выдается, если вы делаете что-то подобное:
let any: Any = ("String", true)
any as! (String, Any) // error
any as! (String, Bool) // obviously succeeds
ошибка:
Не удалось преобразовать значение типа '(Swift.String, Swift.Bool)' в '(protocol<>, protocol<>)'
Так есть ли обходной путь, особенно для второго сценария? Потому что вы не можете даже разыграть Any
к любому кортежу (Any, Any)
где вы могли бы кастовать элементы отдельно.
2 ответа
Кортежи не могут быть разобраны, даже если типы, которые они содержат, могут. Например:
let nums = (1, 5, 9)
let doubleNums = nums as (Double, Double, Double) //fails
Но:
let nums : (Double, Double, Double) = (1, 5, 9) //succeeds
Обходной путь в вашем случае - привести отдельный элемент, а не сам кортеж:
let tuple = ("String", true)
let anyTuple = (tuple.0, tuple.1 as Any)
// anyTuple is (String, Any)
Это одна из причин, по которой документация Swift отмечает:
Кортежи полезны для временных групп связанных значений. Они не подходят для создания сложных структур данных. Если ваша структура данных может сохраняться за пределами временной области, смоделируйте ее как класс или структуру, а не как кортеж.
Я думаю, что это ограничение реализации, потому что кортежи являются составными типами, такими как функции. Точно так же вы не можете создавать расширения Tuples (например, extension (String, Bool) { … }
).
Если вы на самом деле работаете с API, который возвращает (String, Any)
попробуйте изменить его, чтобы использовать класс или структуру. Но если вы не можете улучшить API, вы можете switch
по типу второго элемента:
let tuple : (String, Any) = ("string", true)
switch tuple.1 {
case let x as Bool:
print("It's a Bool")
let boolTuple = (tuple.0, tuple.1 as! Bool)
case let x as Double:
print("It's a Double")
let doubleTuple = (tuple.0, tuple.1 as! Double)
case let x as NSDateFormatter:
print("It's an NSDateFormatter")
let dateFormatterTuple = (tuple.0, tuple.1 as! NSDateFormatter)
default:
print("Unsupported type")
}
Если API возвращает Any
и кортеж не гарантированно будет (String, Any)
, не повезло тебе.
Компилятор Swift обеспечит, чтобы вы явно указывали на ваши типы. Так что, если вы объявите это как (String, Bool)
это не позволит преобразование.
Следующие работы, как и ожидалось, на детской площадке:
var specificTuple : (String, Bool) = ("Hi", false)
var generalTuple : (Any, Any) = ("Hi", false)
var gany = generalTuple
gany.1 = "there"
gany // (.0 "Hi", .1 "there")
var spany = specificTuple
spany.1 = "there" // error
Вы можете создать (Any, Any)
кортеж ad hoc, но вам нужно будет разложить его
var any : (Any, Any) = (specificTuple.0, specificTuple.1)
any.1 = "there"
any // (.0 "Hi", .1 "there")