Not sure I can mentor, but maybe I can give you a good starting direction.
To begin with, you really need to be clear about what problems youāre trying to solve. Some of these proposals have started with āit would be nice if we had thisā, but they include somewhat contrived examples, wishlist items that depend on other major language features (like type-level integers), or arenāt really specific enough about how theyād be used in the real world.
So a good start would be to gather some real-world examples, possibly edited down for clarityās sake, but based on actual code people have written in earnest rather than hypothetical examples. These examples could be Rust code that you find cumbersome, or code in other languages that have some kind of native bitfield support, like C or the bitfield destructuring in Erlang, to show how that native support makes the code a lot more readable.
Once you have some examples and real-world use-cases, it would be good to compare them against existing macro based solutions in Rust, like rust-bitfield, pnet_macros, bitfield-register, or solutions that provide bitflag support like bitflags or enumflags.
If you still find that those donāt meet your needs, or arenāt quite seamless enough to use and still would make the code cumbersome, then it would be a good idea to draft a possible solution. It doesnāt have to be complete, but should show the basics of how it would work, and answer some questions like:
- If there are some kind of special narrow types, can they be used outside of a packed struct? Or is it like C, where the type is a regular type, but thereās a bit width specifier for how many bits its packed into in the struct? Either way, how do you handle overflow (if there are types for bitfields, then arithmetic overflow when manipulating them, if itās C like, then when packing values into it)?
- Would it help more to have bitfields in structures like in C, treated as members of the structure which just have limited things you can do with them like never taking a reference, or is what is needed just the ability to do Erlang-style convenient destructuring and construction of byte arrays?
- Can part of your proposal, or a subset of it, be prototyped using macros, the type system, and operator overloads, even if the syntax isnāt quite as convenient?
- Could all of it be implemented that way if given some other lower level feature, like extensible destructuring and matching support (for the Erlang-style approach), or maybe
std::ops::Assign
that would let you have some kind of dummy type that would let you do pixel.red() = pixel.green()/2 + pixel.blue()/2
?
Here are a couple of starting ideas, as well as everything else discussed in this thread and previous ones, but the real work will be in both finding some real-world examples to see how these work for real problems, and thinking through how all of the corner cases will work and it will fit in with the rest of Rust, including overflow behavior, endianness, whether this will provide a zero-cost abstraction or whether people interested in efficiency will need to do the bit-twiddling by hand.
A new type of repr
:
#[repr(bitpacked(r = "5", g = "6", b = "5"))]
struct RGB565 {
r: u8, g: u8, b: u8,
}
Some special type that repr(packed)
knows can be packed at a sub-byte level:
use std::bitfield::{self, Bitfield, FiveBits, SixBits, Saturate};
#[repr(packed)]
struct RGB565 {
r: Bitfield<u8, FiveBits, Saturate>,
g: Bitfield<u8, SixBits, Saturate>,
b: Bitfield<u8, FiveBits, Saturate>,
}
A macro that could expand into something using std::ops
traits, including a new std::ops::Assign
(not sure this could work, after testing out @hannobraunās suggestion from earlier, you get an āinvalid expression on left-hand sideā for something like color.red() += 1
):
#[bitfields(overflow = "saturate")]
struct RGB565 {
#[bitfield(width = "5")]
r: u8,
#[bitfield(width = "6")]
g: u8,
#[bitfield(width = "5")]
b: u8,
}
Erlang style destructuring support (really unsure of the syntax that would fit in Rust, just throwing out something crazy):
let data: [u8; 2] = [0 as 5bits, 63 as 6bits, 31 as 5bits];
match data {
[r as 5bits, g as 6bits, 0 as 5bits] => ...,
[r as 5bits, g as 6bits, b as 5bits] => ...,
}