CustomStringConvertible в перечислении
У меня есть следующее перечисление в классе.
enum Attributes: String, CustomStringConvertible {
case eventDate
case eventName
case eventType
case country
var description: String {
return self.rawValue
}
}
Когда я пытаюсь получить следующий код, компилятор жалуется на следующую ошибку.
var attributesList: [String] {
return [
Attributes.eventDate, //<-- Compiler error on this row
Attributes.eventName,
Attributes.eventType,
Attributes.country]
}
Невозможно преобразовать значение типа "Атрибуты" в ожидаемый тип элемента "Строка".
Разве протокол CustomStringConvertible не должен возвращать "описание"? Что не так в приведенном выше коде?
1 ответ
TL; DR - это не работает, потому что массив Attribute
s не может быть назначен массиву String
s, они оба являются несовпадающими типами, и Swift не выполняет автоматическое преобразование между типами, и необходимо указать явное преобразование.
В Swift при инициализации массива с использованием литерала массива происходит следующее:
let words = ["hello", "world"]
- Компилятор распознает, что литерал массива назначается переменной с именем
words
, Поскольку мы не указали типwords
, неявно предполагается массив. Тип элементов, лежащих в основе массива, определяется на основе содержимого литерала массива. - В этом случае литерал массива является коллекцией
String
типы; это легко понять компилятору - Поскольку тип LHS является массивом, структура RHS является литералом массива, а поскольку тип LHS (
Array
) соответствует заранее определенному протоколу, называемомуExpressibleByArrayLiteral
который имеет связанное ограничение типа для соответствияElement
, компилятор будет фактически преобразовывать нашу строку в следующую
Пример:
let words = [String].init(arrayLiteral: ["hello", "world"]) // we do not call this init directly
Вот как работает инициализация с массивами литералов. В приведенном выше примере, поскольку мы не указали тип массива, будет работать неявная настройка типа. Если мы указали несоответствующий тип, присвоение завершится неудачно, так как ExpressibleByArrayLiteral
требует ассоциированного Element
тип литерала массива и фактического массива, которому вы назначаете соответствие.
Таким образом, следующее терпит неудачу:
let words:[String] = [1, 2] // array literal has Element=Int, array has Element=String
Это также показывает, что нет неявного преобразования типов между Int
а также String
, даже если Int
соответствовать CustomStringConvertible
,
В вашем случае вы пытаетесь назначить литерал массива, состоящий из Attributes
к массиву String
, Это несоответствие типов. Это причина, почему это не удается.
Если вы заявите о соответствии протокола, будет работать следующая строка:
var attributesList: [CustomStringConvertible] {
return [
Attributes.eventDate,
Attributes.eventName,
Attributes.eventType,
Attributes.country
]
}
// note that we have an array of CustomStringConvertible protocol,
// each element here is still of Attributes type
// a type conforming to a protocol can be cast to an instance
// of that protocol automatically
// The above initialisation works simply because the following
// also works without any further action required
// let a:CustomStringConvertible = Attributes.country
Если вы действительно хотите получить список строковых значений, вам нужно явно отобразить это в строку:
var attributesList1: [String] {
return [
Attributes.eventDate,
Attributes.eventName,
Attributes.eventType,
Attributes.country
].map { $0.description }
}
var attributesList2: [String] {
return [
Attributes.eventDate.description,
Attributes.eventName.description,
Attributes.eventType.description,
Attributes.country.description
]
}