Ответ 1
Изменение: Я удивлен тем, что раньше я не говорил об этом заранее, но... вместо того, чтобы пытаться принудительно использовать другие типы значений в протоколе OptionSet
(Swift 3 удалил Type
из названия), он вероятно, лучше рассмотреть API, в котором вы используете эти типы, и, где это уместно, использовать коллекции Set
.
OptionSet
типы странные. Они оба являются коллекциями, а не коллекциями - вы можете создать один из нескольких флагов, но в результате вы получите одно значение. (Вы можете проделать некоторую работу, чтобы выяснить коллекцию одиночных флагов, эквивалентную такому значению, но в зависимости от возможных значений в типе оно может быть не уникальным.)
С другой стороны, возможность иметь что-то одно или несколько уникальных предметов может иметь важное значение для разработки API. Вы хотите, чтобы пользователи сказали, что у них более одного фаворита, или хотите, чтобы их было только один? Сколько "избранных" вы хотите разрешить? Если пользователь запрашивает несколько избранных, должны ли они быть ранжированы в определенном для него порядке? Это все вопросы, на которые трудно ответить с типом OptionSet
-style, но гораздо проще, если вы используете тип Set
или другую фактическую коллекцию.
Остальная часть этого ответа а) старая, с использованием имен Swift 2, и б) предполагает, что вы все равно пытаетесь реализовать OptionSet
, даже если это плохой выбор для вашего API...
Смотрите документы для OptionSetType
:
Обеспечивает удобное соответствие
SetAlgebraType
для любого типа, чейRawValue
являетсяBitwiseOperationsType
.
Другими словами, вы можете объявить соответствие OptionSetType
для любого типа, который также принимает RawRepresentable
. Однако вы получаете поддержку синтаксиса магической алгебры множеств (через операторы и соответствие ArrayLiteralConvertible
) тогда и только тогда, когда ваш связанный тип необработанных значений соответствует типу BitwiseOperationsType
.
Таким образом, если ваш тип необработанного значения String
, вам не повезло - вы не получаете набор алгебры, потому что String
не поддерживает побитовые операции. (Самое интересное здесь, если вы можете так его назвать, это то, что вы можете расширить String
для поддержки BitwiseOperationsType
, и если ваша реализация удовлетворяет аксиомам axioms, вы можете использовать строки как необработанные значения для набора параметров.)
Ваши вторые синтаксические ошибки во время выполнения, потому что вы создали бесконечную рекурсию - вызов self.init(rawValue:)
из init(rawValue:)
удерживает гонг, пока он не унесет стек.
Возможно, это ошибка (, пожалуйста, подайте ее), которую вы можете попробовать даже без ошибки времени компиляции. Перечисления не должны быть в состоянии объявить соответствие OptionSetType
, потому что:
Семантический контракт перечисления состоит в том, что это закрытое множество. Объявляя ваше перечисление
ProgrammingLanguage
, вы говорите, что значение типаProgrammingLanguage
должно быть одним изSwift
,Scala
илиHaskell
, а не каким-либо другим. Значение "Swift и Scala" не входит в этот набор.Базовая реализация
OptionSetType
основана на целочисленных битовых полях. Значение "Swift и Haskell" ([.Swift, .Haskell]
) действительно просто.Swift.rawValue | .Haskell.rawValue
. Это вызывает проблемы, если ваш набор необработанных значений не выровнен по битам. То есть, если.Swift.rawValue == 1 == 0b01
и.Haskell.rawValue == 2 == 0b10
, побитовый или из них равен0b11 == 3
, что совпадает с.Scala.rawValue
.
TLDR: если вы хотите соответствия OptionSetType
, объявите структуру.
И используйте static let
для объявления членов вашего типа.
И выберите ваши исходные значения так, чтобы элементы, которые вы хотите отличить от возможных (побитовых или) комбинаций других элементов, фактически являются.
struct ProgrammingLanguage: OptionSetType {
let rawValue: Int
// this initializer is required, but it also automatically
// synthesized if 'rawValue' is the only member, so writing it
// here is optional:
init(rawValue: Int) { self.rawValue = rawValue }
static let Swift = ProgrammingLanguage(rawValue: 0b001)
static let Haskell = ProgrammingLanguage(rawValue: 0b010)
static let Scala = ProgrammingLanguage(rawValue: 0b100)
}
Хорошие способы сохранить ваши значения различными: используйте двоично-литеральный синтаксис, как указано выше, или объявите значения с битовыми сдвигами, равными единице, как показано ниже:
static let Swift = ProgrammingLanguage(rawValue: 1 << 0)
static let Haskell = ProgrammingLanguage(rawValue: 1 << 1)
static let Scala = ProgrammingLanguage(rawValue: 1 << 2)