Skip to content

Commit cdd90eb

Browse files
committed
Use a two-type-parameter BitFlags definition
This allows implementing const fn APIs on stable Rust.
1 parent ca880e3 commit cdd90eb

File tree

1 file changed

+81
-6
lines changed

1 file changed

+81
-6
lines changed

src/lib.rs

Lines changed: 81 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@
8484

8585
use core::{cmp, ops};
8686
use core::iter::FromIterator;
87+
use core::marker::PhantomData;
8788

8889
#[allow(unused_imports)]
8990
#[macro_use]
@@ -244,10 +245,84 @@ pub use crate::fallible::FromBitsError;
244245

245246
/// Represents a set of flags of some type `T`.
246247
/// `T` must have the `#[bitflags]` attribute applied.
247-
#[derive(Copy, Clone, Eq, Hash)]
248+
///
249+
/// A `BitFlags<T>` is as large as the `T` itself,
250+
/// and stores one flag per bit.
251+
///
252+
/// ## Memory layout
253+
///
254+
/// `BitFlags<T>` is marked with the `#[repr(transparent)]` trait, meaning
255+
/// it can be safely transmuted into the corresponding numeric type.
256+
///
257+
/// Usually, the same can be achieved by using [`BitFlags::from_bits`],
258+
/// [`BitFlags::from_bits_truncated`] or [`BitFlags::from_bits_unchecked`],
259+
/// but transmuting might still be useful if, for example, you're dealing with
260+
/// an entire array of `BitFlags`.
261+
///
262+
/// Transmuting from a numeric type into `BitFlags` may also be done, but
263+
/// care must be taken to make sure that each set bit in the value corresponds
264+
/// to an existing flag (cf. [`from_bits_unchecked`]).
265+
///
266+
/// For example:
267+
///
268+
/// ```
269+
/// # use enumflags2::{BitFlags, bitflags};
270+
/// #[bitflags]
271+
/// #[repr(u8)] // <-- the repr determines the numeric type
272+
/// #[derive(Copy, Clone)]
273+
/// enum TransmuteMe {
274+
/// One = 1 << 0,
275+
/// Two = 1 << 1,
276+
/// }
277+
///
278+
/// # use std::slice;
279+
/// // NOTE: we use a small, self-contained function to handle the slice
280+
/// // conversion to make sure the lifetimes are right.
281+
/// fn transmute_slice<'a>(input: &'a [BitFlags<TransmuteMe>]) -> &'a [u8] {
282+
/// unsafe {
283+
/// slice::from_raw_parts(input.as_ptr() as *const u8, input.len())
284+
/// }
285+
/// }
286+
///
287+
/// let many_flags = &[
288+
/// TransmuteMe::One.into(),
289+
/// TransmuteMe::One | TransmuteMe::Two,
290+
/// ];
291+
///
292+
/// let as_nums = transmute_slice(many_flags);
293+
/// assert_eq!(as_nums, &[0b01, 0b11]);
294+
/// ```
295+
///
296+
/// ## Implementation notes
297+
///
298+
/// You might expect this struct to be defined as
299+
///
300+
/// ```ignore
301+
/// struct BitFlags<T: BitFlag> {
302+
/// value: T::Numeric
303+
/// }
304+
/// ```
305+
///
306+
/// Ideally, that would be the case. However, because `const fn`s cannot
307+
/// have trait bounds in current Rust, this would prevent us from providing
308+
/// most `const fn` APIs. As a workaround, we define `BitFlags` with two
309+
/// type parameters, with a default for the second one:
310+
///
311+
/// ```ignore
312+
/// struct BitFlags<T, N = <T as BitFlag>::Numeric> {
313+
/// value: N,
314+
/// marker: PhantomData<T>,
315+
/// }
316+
/// ```
317+
///
318+
/// The types substituted for `T` and `N` must always match, creating a
319+
/// `BitFlags` value where that isn't the case is considered to be impossible
320+
/// without unsafe code.
321+
#[derive(Copy, Clone, Hash)]
248322
#[repr(transparent)]
249-
pub struct BitFlags<T: BitFlag> {
250-
val: T::Numeric,
323+
pub struct BitFlags<T, N = <T as _internal::RawBitFlags>::Numeric> {
324+
val: N,
325+
marker: PhantomData<T>,
251326
}
252327

253328
/// The default value returned is one with all flags unset, i. e. [`empty`][Self::empty].
@@ -280,7 +355,7 @@ where
280355
/// The argument must not have set bits at positions not corresponding to
281356
/// any flag.
282357
pub unsafe fn from_bits_unchecked(val: T::Numeric) -> Self {
283-
BitFlags { val }
358+
BitFlags { val, marker: PhantomData }
284359
}
285360

286361
/// Create a `BitFlags` with no flags set (in other words, with a value of `0`).
@@ -341,13 +416,13 @@ where
341416
/// but works in a const context.
342417
///
343418
/// [`empty()`]: #method.empty
344-
pub const EMPTY: Self = BitFlags { val: T::EMPTY };
419+
pub const EMPTY: Self = BitFlags { val: T::EMPTY, marker: PhantomData };
345420

346421
/// A `BitFlags` with all flags set. Equivalent to [`all()`],
347422
/// but works in a const context.
348423
///
349424
/// [`all()`]: #method.all
350-
pub const ALL: Self = BitFlags { val: T::ALL_BITS };
425+
pub const ALL: Self = BitFlags { val: T::ALL_BITS, marker: PhantomData };
351426

352427
/// Returns true if all flags are set
353428
pub fn is_all(self) -> bool {

0 commit comments

Comments
 (0)