New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Considerations for 2.0 #262
Comments
There are some issues that I'd like to see getting fixed, but I'm not sure if that would be a breaking change: |
I'd love to have a well-documented way to construct bitflags in a constant. Right now, I use a macro wrapped around |
In fact, this issue has been a hindrance for me for a long time. I often want to use bitflags when doing FFI wrappers, where I don't necessarily know all the possible flags, but want to keep unknown bits around so I can forward them, serialize them to disk, etc... Or file format parsers where I similarly can't know all the possible flags, but want to make sure I keep the originals around. Having the function marked unsafe is super annoying, especially since there's no mentions of the invariants that need to be upheld. Renaming |
This at least is something we could do now without any breakage. We could just deprecate |
That’s probably the most ergonomic looking way until
A general lack of nice examples is another issue 🙂 |
It would be nice to have some meta information for FFI. Not only the way to iterate, but also names. |
Hi...a colleague pointed me here because I had tried to use bitflags before and I failed to use it well. I can very well believe it's entirely because I am failing to grasp how to use bitflags, but one core use case I could not figure out how to map well into bitflags is the case of individual binary features heterogenously mixed with fields meant to represent an integer number. For example, I have (had?) a CODEC where there could be a configuration word something a bit like this: pub const LM49352_OUTPUT_OPTIONS: u8 = 0x14;
bitflags! {
pub struct OutputOptions: u8 {
const LR_HP_LEVEL_0DB = 0b0000_0000;
const LR_HP_LEVEL_N1P5DB = 0b0000_0010;
const LR_HP_LEVEL_N3DB = 0b0000_0100;
const LR_HP_LEVEL_N6DB = 0b0000_0110;
const LR_HP_LEVEL_N9DB = 0b0000_1000;
const LR_HP_LEVEL_N12DB = 0b0000_1010;
const LR_HP_LEVEL_N15DB = 0b0000_1100;
const LR_HP_LEVEL_N18DB = 0b0000_1110;
const AUX_0DB = 0b0000_0000;
const AUX_N6DB = 0b0001_0000;
const AUX_EARPEACE = 0b0000_0000;
const AUX_LINEOUT = 0b0010_0000;
const LS_LEVEL_0DB = 0b0000_0000;
const LS_LEVEL_4DB = 0b0100_0000;
const LS_LEVEL_8DB = 0b1100_0000;
const LS_LEVEL_12DB = 0b1100_0000;
}
} I could completely see that maybe I missed an obvious feature of bitflags that would have allowed me to define say, bits 1-3 as some integer value that could be masked and shifted into the right spot, but I couldn't seem to find any mention of that in the docs. When we had to port to a new codec, I tried playing around with using bitflags as some way to just keep a mask and a bit offset, but it felt like a lot of overhead compared to just defining a couple consts and doing shifts and adds, because I was ultimately taking everything out of a bitflag and casting it to an int and then doing the operation, instead of taking advantage of the neat operator overloading that bitflags has built in. But maybe that's because I'm an old school C programmer who just finds that kind of mask-and-shift idiom very natural, and closures to be clumsy... Anyways, a colleague had pointed me to this thread since it looked like feedback was solicited on bitflags, and I thought I'd share a use case that I found a bit hard to be covered by the API. Maybe my use case should just be out of scope, because (for example) extending bitflags to take a closure that could map integer fields onto something else (a bit like the PAC crate does) probably looks pretty syntactically cool and powerful but it's also very non-obvious to me that it would compile down to what you're looking for, e.g. just an integer that you're looking to jam into a register. fwiw, as a matter of preference I also did not find the PAC crate to be very ergonomic because a lot of times hardware implementations did not really want to map nicely onto its abstractions and I was constantly finding myself going to godbolt to debug/confirm that my code was generating what I intended it to....99% of the time it's great but I think it's not a great sign for an API if you have to keep a godbolt instance around to make sure it's generating the intended code without accidental extra complexity or misunderstandings because of ambiguities in the syntax. In other words, I'm not advocating something as complicated as PAC, either, as a solution to this problem. |
@bunnie What you want looks more akin to bitfields than bitflags. The way I do this is by combining bitfield and bitflags crate: #[repr(u8)]
enum LrHpLevel {
0DB, N1P5DB, N3DB, // ...
}
#[repr(u8)]
enum LsLevel {
0DB, 4DB // ...
}
bitflags! {
pub struct AuxFlags: u8 {
const AUX_0DB = 0b00;
const AUX_N6DB = 0b01;
const AUX_EARPEACE = 0b00;
const AUX_LINEOUT = 0b10;
}
}
bitfield!{
pub struct OutputOptions(u8);
impl Debug;
u8, from into LrHpLevel, lr_hp_level, set_lr_hp_level: 3, 1;
u8, from into AuxFlags, aux_flags, set_aux_flags: 5, 4;
u8, from into LsLevel, ls_level, set_ls_level, 7, 6;
} What might be a good idea is making sure those two crates interoperate better, and maybe provide some documentation, as it is a rather common use-case in the embedded world. Also, eventually I hope bitfields will be handled in the language proper (see rust-lang/rfcs#314) in which case bitflags should hopefully work as-is. |
Ahah! The feature I was looking for was hiding in another crate, in plain sight, all along. thanks! |
@roblabla that could make a great example in |
I'd like Bitflags to also work for unrecognized flags without that being unsafe (#228 ) |
I've created a https://github.com/bitflags/bitflags/tree/release/1.x branch that non-breaking changes can continue to work their way into so So here's what I'm thinking as a plan for Replacing
|
I've started sketching out an alternative implementation for a bitflags! {
+ #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
struct Flags: u32 {
const A = 0b00000001;
const B = 0b00000010;
const C = 0b00000100;
- const ABC = Flags::A.bits | Flags::B.bits | Flags::C.bits;
+ const ABC = Flags::A.bits() | Flags::B.bits() | Flags::C.bits();
}
} If you want to derive This gives you a chance to change the way you implement traits like How do people feel about this? I don't want to create churn that doesn't seem worthwhile, but if it's palatable then it gives us a way to naturally tweak trait implementations. There's a middle-ground here where we would include bitflags! {
+ #[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
struct Flags: u32 {
const A = 0b00000001;
const B = 0b00000010;
const C = 0b00000100;
- const ABC = Flags::A.bits | Flags::B.bits | Flags::C.bits;
+ const ABC = Flags::A.bits() | Flags::B.bits() | Flags::C.bits();
}
} What do you all think? |
For my own use case, making it safe to include unknown bits in the flags could actually introduce UB. It relies on not receiving any unrecognised bits, because it needs to check the validity of each bit before passing it on to a foreign API (Vulkan). Unrecognised bits could potentially violate the API if left, and if the user could introduce an unchecked bit safely, then that could lead to UB in safe code, which goes against what Rust tries to do. Having no guarantee that the bitflags values received by functions are all valid in safe code, would mean that every Rust function has to run every bitflags type it receives through So, please keep |
Maybe an attribute could be given like |
Hi, I am pretty new to Rust and could not find if what I am about to propose is possible. const SPECIFIC_FLAG: TheBitflagsType = TheBitflagsType::Flag1 | TheBitflagsType::Flag2).union | … ; Is nicer and more intuitive than: const SPECIFIC_FLAG: TheBitflagsType = TheBitflagsType::Flag1.union(TheBitflagsType::Flag2).union(…); So my question and suggestion is: impl $crate::_core::ops::BitOr for $BitFlags {
type Output = Self;
/// Returns the union of the two sets of flags.
#[inline]
fn bitor(self, other: $BitFlags) -> Self {
Self { bits: self.bits | other.bits }
}
} Could changing fn bitor(self, other: $BitFlags) -> Self { to const fn bitor(self, other: $BitFlags) -> Self { work and make it useable in constant ? |
It is currently impossible to use traits from const functions, including the operator trait, in stable rust. The operators on integers/floats/etc... are special cased to work in const context. So, no, unfortunately, that is not currently possible. |
@roblabla |
@Rua is there a repository somewhere with your use-case I could take a look at? |
can you allow to add attribute for field |
Hi @djdisodo 👋 Could you elaborate a bit on what you mean? Which attributes would you like to add to the |
@KodrAus any attribute, just like current bitflags macro handles attributes that will be applied to expanded struct i'm planning to use helper attribute to the field |
@djdisodo Hmm, the plan for |
just here to express support for serializing to text in human readable formats like json. |
Somewhat related to
I'd like to have something like fn bits_mut(&mut self) -> &mut u16; Currently I am using an enum to switch between |
Hey all! 👋 Things have been moving slowly but surely here, and now have #282 that splits responsibilities between the user-facing API and the In bitflags! {
pub struct Flags: u32 {
const A = 0b00000001;
const B = 0b00000010;
const C = 0b00000100;
const ABC = Flags::A.bits | Flags::B.bits | Flags::C.bits;
}
} In bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Flags: u32 {
const A = 0b00000001;
const B = 0b00000010;
const C = 0b00000100;
const ABC = Flags::A.bits() | Flags::B.bits() | Flags::C.bits();
}
} to get an equivalent flags type. You can still generate flags without the bitflags! {
pub struct Flags: u32 {
const A = 0b00000001;
const B = 0b00000010;
const C = 0b00000100;
const ABC = Flags::A.bits() | Flags::B.bits() | Flags::C.bits();
}
} and you'll get some set-operators, but none of the traits you'd normally derive, just as if you were writing a plain-old-struct. If you didn't want your flags type to derive This new approach gives us more flexibility, but it comes at a cost: you have to do more work when generating flags types. I'm conscious of how easy it can be to trade ergonomics for flexibility to the point that nobody finds upgrading worthwhile. So I'd love to hear from anyone out there with flags types they'd now need to start peppering There's a sliding scale of how far down this path we want to go. I've opted for "any trait that can be |
I don't think adding derive statements are that annoying but if its an issue, you could introduce a second macro, e.g. |
I've merged #282 which does most of the internal refactoring to let us separate the API The last thing we need to do before being able to put out a pre-release is make the iterators we generate for enabled flags a concrete type instead of |
I've just published a
I think this is an exciting release and am keen for any feedback on how you all find it! |
does that mean serializing to json or other text formats will just serialize the number representing bits rather than text names? |
@coderedart If you previously derived We don’t have a parser for the |
Since we do already require the |
instead of or even a sequence As this is a breaking semver anyway, its the best time to adopt such a change (or explicitly reject it). |
It would be easier to use a sequence for sure. I think a reason to have a canonical string format is that it would open up support for opting in to |
In #297 I've started work on a text parser for flags formatted using bar-separated-names, like:
That PR also updates our default
|
|
Missing the fact that your serialization defaults will change when you bump the library version is definitely a concern for me. My own opinion is that we should change the default while we have some reasonable chance to do so, call it out explicitly in the release notes along with a demonstration of how to keep the existing behavior if you rely on it. We’re moving from an unconsidered default of serializing to a struct with an integer field to a considered (and I think entirely superior) one of serializing to a formatted string for end-users or the underlying bits directly for compact formats. #298 is an example of where the current default is simply unsuitable for compact users and #147 is an example for end-users. |
Hi, As a new user, I can say I was really puzzled when my embedded systems stop working when I added some bitflags in my messages. It didn't took long to find that the deserialization was raising errors, and then it didn't took long to find that only the messages with bitflags were raising errors, and then it didn't took long to workaround conversions from and to So obviously I'm biased, but even if I understand that changing the default is not an easy decision to take, I'd be in favor of doing it 😅 |
I've opened #299 to push a new RC release that includes the change to the default serialization format. It will now use a formatted string for human-readable formats and the underlying bits To keep the old behavior, see the |
If everything looks good, I'll leave this release candidate around for a few weeks and publish it as a stable |
|
Before we actually release a build with #220 I think it’s a good time to consider any breaking changes that could work their way into a
2.0
release. Without much public surface area I think the risk of multiplebitflags
versions in a dependency graph is low.Some I had in mind:
from_bits_unchecked
unsafe. The idea was to make it unsafe to construct flags using bits that don’t correspond to flags specified. That way an implementor could consider using things likeunreachable_unchecked
in code that matches on bits. In hindsight this just seems like something people work around rather than make use of. Since we generate types in the scope of thebitflags!
invocation there’s already nothing stopping someone from manually stuffing extra bits in a flags type. So we can’t rely onfrom_bits_unchecked
inbitflags
code either.#[bitflags_derive]
that can be used to add them. That way callers can decide on their own equality and debug implementations if they want.The text was updated successfully, but these errors were encountered: