Today I tried playing bit with OptionSet in Playground, and I notice this pattern
struct Activities: OptionSet {
let rawValue: Int
static let eating = Activities(rawValue: 1)
static let programming = Activities(rawValue: 2)
static let breathing = Activities(rawValue: 3)
static let saveGotham = Activities(rawValue: 4)
}
let act: Activities = [.eating, .programming, .saveGotham]
act.contains(.breathing). //true /* this is unexpected */
act.contains(.saveGotham) //true
Although, the array doesn't contain the value '.breathing' it still return true. I modified the same struct with different rawValue
struct Activities: OptionSet {
let rawValue: Int
static let eating = Activities(rawValue: 1)
static let programming = Activities(rawValue: 8)
static let breathing = Activities(rawValue: 16)
static let saveGotham = Activities(rawValue: 32)
}
let act: Activities = [.eating, .programming, .saveGotham]
act.contains(.breathing). //false
act.contains(.saveGotham) //true
and got the desired output. it would be awesome if someone shed the light on the problem and explain how the 'OptionSet' actually works.
Thank you.
The OptionSet protocol is meant to
... represent bitset types, where individual bits represent members of a set.
In your case,
let act: Activities = [.eating, .programming, .saveGotham]
print(act.rawValue) // 7
is stored as an integer containing the BITWISE OR of the raw values
(1 | 2 | 4 = 7)
, and
act.contains(.breathing). //true /* this is unexpected */
tests if the BITWISE AND 7 & 3
is non-zero (which is the case).
Therefore you should not use consecutive raw values, but powers of two, i.e. each of the mutually exclusive values is represented by one bit position:
struct Activities: OptionSet {
let rawValue: Int
static let eating = Activities(rawValue: 1)
static let programming = Activities(rawValue: 2)
static let breathing = Activities(rawValue: 4)
static let saveGotham = Activities(rawValue: 8)
}
or equivalently:
struct Activities: OptionSet {
let rawValue: Int
static let eating = Activities(rawValue: 1 << 0)
static let programming = Activities(rawValue: 1 << 1)
static let breathing = Activities(rawValue: 1 << 2)
static let saveGotham = Activities(rawValue: 1 << 3)
}
Now everything works as expected:
let act: Activities = [.eating, .programming, .saveGotham]
print(act.rawValue) // 11
print(act.contains(.breathing)) // false
print(act.contains(.saveGotham)) // true