Based on https://developer.apple.com/documentation/swift/optionset , I notice that it is not possible to have more than 64 members in OptionSet
For instance
struct Options: OptionSet {
let rawValue: Int
static let options0 = Options(rawValue: 1 << 0)
static let options1 = Options(rawValue: 1 << 1)
static let options2 = Options(rawValue: 1 << 2)
static let options3 = Options(rawValue: 1 << 3)
// ...
// ...
// ...
static let options63 = Options(rawValue: 1 << 63)
// rawValue = 0
static let options64 = Options(rawValue: 1 << 64)
// rawValue = 0
static let options65 = Options(rawValue: 1 << 65)
}
let options: Options = [.options64]
precondition(!options.contains(.options65))
For the above code example, the set shouldn’t contain option65 as it is not inserted initially.
However, since both 1 << 64
and 1 << 65
ended up as 0, this will yield wrong result.
I was wondering, is this the limitation of OptionSet
of not able to have more than 64 members? Or, there is a workaround?
2
Answers
Although Swift only provides fixed width integer types for up to 64 bits,
OptionSet
only requires thatRawValue
to be any kind ofFixedWidthInteger
for the defaultSetAlgebra
implementations to be synthesised.Therefore, you can technically always create your
FixedWidthInteger
of arbitrary size, by combining smaller integers, and use it in an option set.For example, here is a
UInt128
implementation I found. I can use it in an option set just like normal:Though obviously, this is not going to be as fast as the natively supported integers, and admittedly, if someone hasn’t already implemented the size you want, implementing a whole
FixedWidthInteger
just for anOptionSet
isn’t very practical.An
OptionSet
needs to implement the methodsfrom the
SetAlgebra
protocol, and the operatorfrom the
Equatable
protocol.If the
rawValue
is a type that conforms to theFixedWidthInteger
protocol then the standard library provides default implementations for all these requirements. So one option (as suggested by Sweeper) is to use an integer type with more than 64 bits.But actually not the full power of
FixedWidthInteger
is needed forOptionSet
, only the bitwise AND, OR, and XOR operations. So here is another possible solution:First define a protocol for the required bitwise operations:
(Remark: There used to be a
BitwiseOperation
protocol in Swift 3, but that does not exist anymore.)Next we define default implementations for all required
OptionSet
methods if the raw value conforms to theBitwiseOperators
type:Now, in order to define an option set with more than 64 possible options, we need a raw value type which can store the necessary number of bits, and implements the AND, OR, and XOR operations. Here is an example for 128 bits, but it can easily be extended to any necessary size:
As a convenience we can define an initializer which sets exactly one bit at a given position:
Finally we can define the
Options
options set withRaw128
as the raw value type:and everything works as expected: