Skip to content

Commit

Permalink
Expand traits implemented by BitFlags
Browse files Browse the repository at this point in the history
PartialOrd and Ord are now implemented. The values returned by the
traits aren't meaningful, but they allow using BitFlags as a key
in a BTreeMap. I wish the traits had a distinction between
"meaningful order" (overloaded comparison operators) and "arbitrary
order" (can be used in BTreeMap).

Moreover, the implementation for Hash is improved, so that code
that is generic over BitFlag can still access it.

Closes #47
  • Loading branch information
meithecatte committed Sep 13, 2023
1 parent b61a59e commit 5231436
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 18 deletions.
28 changes: 24 additions & 4 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@
#![warn(missing_docs)]
#![cfg_attr(all(not(test), not(feature = "std")), no_std)]

use core::hash::{Hash, Hasher};
use core::iter::{FromIterator, FusedIterator};
use core::marker::PhantomData;
use core::{cmp, ops};
Expand Down Expand Up @@ -218,6 +219,7 @@ pub mod _internal {
use ::core::cmp::PartialOrd;
use ::core::fmt;
use ::core::ops::{BitAnd, BitOr, BitXor, Not, Sub};
use ::core::hash::Hash;

pub trait BitFlagNum:
Default
Expand All @@ -227,6 +229,8 @@ pub mod _internal {
+ Sub<Self, Output = Self>
+ Not<Output = Self>
+ PartialOrd<Self>
+ Ord
+ Hash
+ fmt::Debug
+ fmt::Binary
+ Copy
Expand Down Expand Up @@ -373,7 +377,7 @@ pub use crate::fallible::FromBitsError;
/// The types substituted for `T` and `N` must always match, creating a
/// `BitFlags` value where that isn't the case is only possible with
/// incorrect unsafe code.
#[derive(Copy, Clone, Eq)]
#[derive(Copy, Clone)]
#[repr(transparent)]
pub struct BitFlags<T, N = <T as _internal::RawBitFlags>::Numeric> {
val: N,
Expand Down Expand Up @@ -876,17 +880,33 @@ for_each_uint! { $ty $hide_docs =>
}
}

impl<T, N: PartialEq> cmp::PartialEq for BitFlags<T, N> {
impl<T, N: PartialEq> PartialEq for BitFlags<T, N> {
#[inline(always)]
fn eq(&self, other: &Self) -> bool {
self.val == other.val
}
}

impl<T, N: Eq> Eq for BitFlags<T, N> {}

impl<T, N: PartialOrd> PartialOrd for BitFlags<T, N> {
#[inline(always)]
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
self.val.partial_cmp(&other.val)
}
}

impl<T, N: Ord> Ord for BitFlags<T, N> {
#[inline(always)]
fn cmp(&self, other: &Self) -> cmp::Ordering {
self.val.cmp(&other.val)
}
}

// Clippy complains when Hash is derived while PartialEq is implemented manually
impl<T, N: core::hash::Hash> core::hash::Hash for BitFlags<T, N> {
impl<T, N: Hash> Hash for BitFlags<T, N> {
#[inline(always)]
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
fn hash<H: Hasher>(&self, state: &mut H) {
self.val.hash(state)
}
}
Expand Down
33 changes: 19 additions & 14 deletions test_suite/tests/requires_std.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
use enumflags2::{bitflags, BitFlags};
#![allow(dead_code)]
use enumflags2::{bitflags, BitFlag, BitFlags};

include!("../common.rs");

#[test]
fn debug_format() {
use enumflags2::BitFlags;

// Assert that our Debug output format meets expectations

assert_eq!(
Expand Down Expand Up @@ -63,8 +62,6 @@ fn debug_format_alternate() {

#[test]
fn display_format() {
use enumflags2::BitFlags;

// Assert that our Debug output format meets expectations

assert_eq!(
Expand All @@ -85,8 +82,6 @@ fn display_format() {

#[test]
fn format() {
use enumflags2::BitFlags;

// Assert BitFlags<T> impls fmt::{Binary, Octal, LowerHex, UpperHex}

assert_eq!(format!("{:b}", BitFlags::<Test>::all()), "1111");
Expand All @@ -100,18 +95,28 @@ fn format() {

#[test]
fn debug_generic() {
use enumflags2::{BitFlag, BitFlags};

#[derive(Debug)]
struct Debug<T: BitFlag>(BitFlags<T>);

let _ = format!("{:?}", Debug(BitFlags::<Test>::all()));
}

#[test]
fn works_in_hashmap() {
// Assert that BitFlags<T> implements Hash.
fn works_in_maps() {
// Assert that BitFlags<T> implements Hash and Ord.

use std::collections::{BTreeSet, HashSet};
let mut map: BTreeSet<BitFlags<Test>> = BTreeSet::new();
map.insert(BitFlags::empty());
let mut map: HashSet<BitFlags<Test>> = HashSet::new();
map.insert(BitFlags::empty());
}

fn works_in_maps_generic<T: BitFlag>() {
// Assert that BitFlags<T> implements Hash and Ord.

use std::collections::HashMap;
let _map: HashMap<BitFlags<Test>, u8> = HashMap::new();
use std::collections::{BTreeSet, HashSet};
let mut map: BTreeSet<BitFlags<T>> = BTreeSet::new();
map.insert(BitFlags::empty());
let mut map: HashSet<BitFlags<T>> = HashSet::new();
map.insert(BitFlags::empty());
}

0 comments on commit 5231436

Please sign in to comment.