-
Notifications
You must be signed in to change notification settings - Fork 24
Description
When writing test assertions for bitflags, I found myself wanting to report the missing flags and found that this library has no set difference. Now, arguably let missing = expected & !flags isn't that hard to write, but it seems a common enough operation that it should just get a name (and as soon as there are more than one operator in a set expression, I get confused, esp if there's negation involved).
I've played around a bit and included a patch. With this, you can say
let missing = expected % flags;diff --git a/src/lib.rs b/src/lib.rs
index f41af2e..4a9bb78 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -982,6 +982,19 @@ where
}
}
+impl<T, B> ops::Rem<B> for BitFlags<T>
+where
+ T: BitFlag,
+ B: Into<BitFlags<T>>,
+{
+ type Output = BitFlags<T>;
+ #[inline(always)]
+ fn rem(self, other: B) -> Self::Output {
+ // SAFETY: The two operands are known to be composed of valid bits,
+ // and 0 & 0 = 0 in the columns of the invalid bits.
+ unsafe { BitFlags::from_bits_unchecked(self.bits() & !other.into().bits()) }
+ }
+}
impl<T, B> ops::BitOrAssign<B> for BitFlags<T>
where
T: BitFlag,
@@ -1014,6 +1027,17 @@ where
}
}
+impl<T, B> ops::RemAssign<B> for BitFlags<T>
+where
+ T: BitFlag,
+ B: Into<BitFlags<T>>,
+{
+ #[inline(always)]
+ fn rem_assign(&mut self, other: B) {
+ *self = *self % other
+ }
+}
+
impl<T> ops::Not for BitFlags<T>
where
T: BitFlag,
diff --git a/test_suite/common.rs b/test_suite/common.rs
index e08e0fc..9a9cb22 100644
--- a/test_suite/common.rs
+++ b/test_suite/common.rs
@@ -66,7 +66,10 @@ fn test_ctors() {
#[test]
fn test_ops() {
assert_eq!(!Test::A, Test::B | Test::C | Test::D);
+ assert_eq!((Test::A | Test::C) & (Test::C | Test::B), Test::C);
assert_eq!((Test::A | Test::C) ^ (Test::C | Test::B), Test::A | Test::B);
+ assert_eq!((Test::A | Test::B) % (Test::C | Test::B), Test::A);
+ assert_eq!((Test::C | Test::B) % (Test::A | Test::B), Test::C);
assert!((Test::A | Test::B).intersects(Test::B));
assert!(!(Test::A | Test::B).intersects(Test::C));
assert!((Test::A | Test::B | Test::C).contains(Test::A | Test::B));I'm also happy to open a PR if you don't want to apply the patch yourself, or this needs more work. But I didn't want to go through the whole rigmarole just to gauge your interest.
One obvious concern is that % isn't a bitwise operator, so maybe we're getting into operator abuse territory here (I half expect this is the reason that this lib doesn't have set difference to begin with). In that case, how do you feel about named set operations?
/// Flags in `self` that are not in `other`
#[inline(always)]
#[doc(alias = "relative_complement")]
pub fn difference<B: Into<BitFlags<T>>>(self, other: B) -> Self {
self & !(other.into())
}
/// Flags in either `self` or `other`, but not both
///
/// Synonym for `self ^ other`
#[inline(always)]
pub fn symmetric_difference<B: Into<BitFlags<T>>>(self, other: B) -> Self {
self ^ other
}
/// Flags both in `self` and `other`
///
/// Synonym for `self & other`
#[inline(always)]
pub fn intersection<B: Into<BitFlags<T>>>(self, other: B) -> Self {
self & other
}
/// All Flags in `self` and `other` combined
///
/// Synonym for `self | other`
#[inline(always)]
pub fn union<B: Into<BitFlags<T>>>(self, other: B) -> Self {
self | other
}
/// All Flags not in `self`
///
/// Synonym for `!self`
#[inline(always)]
pub fn complement(self) -> Self {
!self
}