/// * `#[enumset(crate_name = "enumset2")]` may be used to change the name of the `enumset` crate
/// used in the generated code. When the `std` feature is enabled, enumset parses `Cargo.toml`
/// to determine the name of the crate, and this flag is unnecessary.
+/// * `#[enumset(repr = "u8")]` may be used to specify the in-memory representation of `EnumSet`s
+/// of this enum type. The effects of this are described in [the `EnumSet` documentation under
+/// “FFI, Safety and `repr`”][EnumSet#ffi-safety-and-repr]. Allowed types are `u8`, `u16`, `u32`,
+/// `u64` and `u128`. If this is not used, then the derive macro will choose a type to best fit
+/// the enum, but there are no guarantees about which type will be chosen.
///
/// When the `serde` feature is used, the following features may also be specified. These options
/// may be used (with no effect) when building without the feature enabled:
/// [`#[derive(EnumSetType)]`](./derive.EnumSetType.html).
pub unsafe trait EnumSetType: Copy + Eq + EnumSetTypePrivate { }
+/// An [`EnumSetType`] for which [`EnumSet`]s have a guaranteed in-memory representation.
+///
+/// An implementation of this trait is generated by using
+/// [`#[derive(EnumSetType)]`](./derive.EnumSetType.html) with the annotation
+/// `#[enumset(repr = "…")]`, where `…` is `u8`, `u16`, `u32`, `u64` or `u128`.
+///
+/// For any type `T` that implements this trait, the in-memory representation of `EnumSet<T>`
+/// is guaranteed to be `Repr`. This guarantee is useful for FFI. See [the `EnumSet` documentation
+/// under “FFI, Safety and `repr`”][EnumSet#ffi-safety-and-repr] for an example.
+pub unsafe trait EnumSetTypeWithRepr: EnumSetType + EnumSetTypePrivate<Repr = <Self as EnumSetTypeWithRepr>::Repr> {
+ /// The guaranteed representation.
+ type Repr: EnumSetTypeRepr;
+}
+
/// An efficient set type for enums.
///
/// It is implemented using a bitset stored using the smallest integer that can fit all bits
/// In addition, the `#[enumset(serialize_as_list)]` attribute causes the `EnumSet` to be
/// instead serialized as a list of enum variants. This requires your enum type implement
/// [`Serialize`] and [`Deserialize`]. Note that this is a breaking change.
+///
+/// # FFI, Safety and `repr`
+///
+/// If an enum type `T` is annotated with [`#[enumset(repr = "R")]`][derive@EnumSetType#options], then
+/// several things happen:
+///
+/// * `T` will implement <code>[EnumSetTypeWithRepr]<Repr = R></code> in addition to
+/// [`EnumSetType`].
+/// * The `EnumSet` methods with `repr` in their name, such as [`as_repr`][EnumSet::as_repr] and
+/// [`from_repr`][EnumSet::from_repr], will be available for `EnumSet<T>`.
+/// * The in-memory representation of `EnumSet<T>` is guaranteed to be `R`.
+///
+/// That last guarantee makes it sound to send `EnumSet<T>` across an FFI boundary. For example:
+///
+/// ```
+/// # use enumset::*;
+/// #
+/// # mod ffi_impl {
+/// # // This example “foreign” function is actually written in Rust, but for the sake
+/// # // of example, we'll pretend it's written in C.
+/// # #[no_mangle]
+/// # extern "C" fn some_foreign_function(set: u32) -> u32 {
+/// # set & 0b100
+/// # }
+/// # }
+/// #
+/// extern "C" {
+/// // This function is written in C like:
+/// // uint32_t some_foreign_function(uint32_t set) { … }
+/// fn some_foreign_function(set: EnumSet<MyEnum>) -> EnumSet<MyEnum>;
+/// }
+///
+/// #[derive(Debug, EnumSetType)]
+/// #[enumset(repr = "u32")]
+/// enum MyEnum { A, B, C }
+///
+/// let set: EnumSet<MyEnum> = enum_set!(MyEnum::A | MyEnum::C);
+///
+/// let new_set: EnumSet<MyEnum> = unsafe { some_foreign_function(set) };
+/// assert_eq!(new_set, enum_set!(MyEnum::C));
+/// ```
+///
+/// When an `EnumSet<T>` is received via FFI, all bits that don't correspond to an enum variant
+/// of `T` must be set to 0. Behavior is **undefined** if any of these bits are set to 1.
#[derive(Copy, Clone, PartialEq, Eq)]
#[repr(transparent)]
pub struct EnumSet<T: EnumSetType> {
pub fn iter(&self) -> EnumSetIter<T> {
EnumSetIter::new(*self)
}
+
+ /// Returns a `T::Repr` representing the elements of this set.
+ ///
+ /// Unlike the other `as_*` methods, this method is zero-cost and guaranteed not to fail,
+ /// panic or truncate any bits.
+ ///
+ /// In order to use this method, the definition of `T` must have the `#[enumset(repr = "…")]`
+ /// annotation.
+ #[inline(always)]
+ pub fn as_repr(&self) -> <T as EnumSetTypeWithRepr>::Repr
+ where
+ T: EnumSetTypeWithRepr,
+ {
+ self.__priv_repr
+ }
+
+ /// Constructs a bitset from a `T::Repr` without checking for invalid bits.
+ ///
+ /// Unlike the other `from_*` methods, this method is zero-cost and guaranteed not to fail,
+ /// panic or truncate any bits, provided the conditions under “Safety” are upheld.
+ ///
+ /// In order to use this method, the definition of `T` must have the `#[enumset(repr = "…")]`
+ /// annotation.
+ ///
+ /// # Safety
+ ///
+ /// All bits in the provided parameter `bits` that don't correspond to an enum variant of
+ /// `T` must be set to 0. Behavior is **undefined** if any of these bits are set to 1.
+ #[inline(always)]
+ pub unsafe fn from_repr_unchecked(bits: <T as EnumSetTypeWithRepr>::Repr) -> Self
+ where
+ T: EnumSetTypeWithRepr,
+ {
+ Self { __priv_repr: bits }
+ }
+
+ /// Constructs a bitset from a `T::Repr`.
+ ///
+ /// If a bit that doesn't correspond to an enum variant is set, this
+ /// method will panic.
+ ///
+ /// In order to use this method, the definition of `T` must have the `#[enumset(repr = "…")]`
+ /// annotation.
+ #[inline(always)]
+ pub fn from_repr(bits: <T as EnumSetTypeWithRepr>::Repr) -> Self
+ where
+ T: EnumSetTypeWithRepr,
+ {
+ Self::try_from_repr(bits).expect("Bitset contains invalid variants.")
+ }
+
+ /// Attempts to constructs a bitset from a `T::Repr`.
+ ///
+ /// If a bit that doesn't correspond to an enum variant is set, this
+ /// method will return `None`.
+ ///
+ /// In order to use this method, the definition of `T` must have the `#[enumset(repr = "…")]`
+ /// annotation.
+ #[inline(always)]
+ pub fn try_from_repr(bits: <T as EnumSetTypeWithRepr>::Repr) -> Option<Self>
+ where
+ T: EnumSetTypeWithRepr,
+ {
+ let mask = Self::all().__priv_repr;
+ if bits.and_not(mask).is_empty() {
+ Some(EnumSet { __priv_repr: bits })
+ } else {
+ None
+ }
+ }
+
+ /// Constructs a bitset from a `T::Repr`, ignoring invalid variants.
+ ///
+ /// In order to use this method, the definition of `T` must have the `#[enumset(repr = "…")]`
+ /// annotation.
+ #[inline(always)]
+ pub fn from_repr_truncated(bits: <T as EnumSetTypeWithRepr>::Repr) -> Self
+ where
+ T: EnumSetTypeWithRepr,
+ {
+ let mask = Self::all().as_repr();
+ let bits = bits & mask;
+ EnumSet { __priv_repr: bits }
+ }
}
/// Helper macro for generating conversion functions.