Skip to content

Support ejecting flags types from the bitflags macro#351

Merged
KodrAus merged 28 commits intobitflags:mainfrom
KodrAus:feat/public-traits
May 17, 2023
Merged

Support ejecting flags types from the bitflags macro#351
KodrAus merged 28 commits intobitflags:mainfrom
KodrAus:feat/public-traits

Conversation

@KodrAus
Copy link
Member

@KodrAus KodrAus commented May 4, 2023

Closes #348

This PR lets you define your flags type outside of the bitflags! macro, but still optionally use it to generate constants, methods, and trait impls for your flags types:

// First: Define your flags type. It needs to be a newtype over its underlying bits type
pub struct ManualFlags(u32);

// Next: use `impl Flags` instead of `struct Flags`
bitflags! {
    impl ManualFlags: u32 {
        const A = 0b00000001;
        const B = 0b00000010;
        const C = 0b00000100;
        const ABC = Self::A.bits() | Self::B.bits() | Self::C.bits();
    }
}

The BitFlags trait that can't be manually implemented has also been deprecated in favour of a semver-compatible Flags trait that can be manually implemented. This makes it possible to define flags types without using any macros at all:

// First: Define your flags type. It just needs to be `Sized + 'static`.
pub struct ManualFlags(u32);

// Not required: Define some constants for valid flags
impl ManualFlags {
    pub const A: ManualFlags = ManualFlags(0b00000001);
    pub const B: ManualFlags = ManualFlags(0b00000010);
    pub const C: ManualFlags = ManualFlags(0b00000100);
    pub const ABC: ManualFlags = ManualFlags(0b00000111);
}

// Next: Implement the `BitFlags` trait, specifying your set of valid flags
// and iterators
impl Flags for ManualFlags {
    const FLAGS: &'static [Flag<Self>] = &[
        Flag::new("A", Self::A),
        Flag::new("B", Self::B),
        Flag::new("C", Self::C),
    ];

    type Bits = u32;

    fn bits(&self) -> u32 {
        self.0
    }

    fn from_bits_retain(bits: u32) -> Self {
        Self(bits)
    }
}

@KodrAus KodrAus changed the title Make it possible to implement BitFlags manually Support ejecting flags types from the bitflags macro May 5, 2023
@KodrAus
Copy link
Member Author

KodrAus commented May 5, 2023

Since we can't yet replace our const functions with default impls from the Flags trait we currently have 2 copies of each method. These will need a really good test to make sure they're equivalent.

@KodrAus KodrAus marked this pull request as ready for review May 5, 2023 05:38
@KodrAus
Copy link
Member Author

KodrAus commented May 5, 2023

Before merging this, we'll need to introduce a bound that ensures you can't use a custom implementation of Bits in types defined through the bitflags macro. Otherwise crate features in bitflags may introduce additional trait bounds that aren't satisfied by those custom types.

@KodrAus
Copy link
Member Author

KodrAus commented May 6, 2023

Just need to come up with a 1.56.0 compatible method of asserting a custom Bits type is a Rust primitive. That will probably just be a private trait instead of a function call.

@KodrAus KodrAus merged commit c947503 into bitflags:main May 17, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Allow external impls of Bits and BitFlags

1 participant