Кортеж "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")
Другие вопросы по тегам