I have noticed that there have been several attempts at getting things like bit-fields within Rust. Such as n-bit numbers but these have either been closed for later or decided that it was too complicated.
So this post is to facilitate discussion on what sort of things would be wanted with these sort of fields.
Descriptiveness:
- C/C++ have bitfields which have a standard format
type name : n-bits;
. This is good as it is very similar to how other fields are defined in those languages. In rust this might look something likename : type : n-bits;
- From documentation, the fields are generally defined not as a list of field with sizes but as labels to specific bits or range of bits within the registers. In rust this might look like this:
name : 1 as type;
orname : type 3..=6 as type;
.
So within rust we could use either and there are pros and cons to either. With the C/C++ version we have a more recognizable syntax and means of describing the layout. But I would argue that when using bitfields the location of the field is more important then how large it is. Using a different syntax also allows Rust to be easier to read in relation to documentation.
The size of these structures are also a factor when talking about how to layout the data. In C/C++ the size of the structure is the sum of the number of bits rounded up (generally to bytes or words). This makes sense to have a similar restriction in rust but it might also make sense for bitfields to have a predefined maximum size that is separate to that of the layout. This is especially true if opting for a range based layout since the rest of the bits of a 64-bit register might be ignored or reserved for later use.
Proposal:
// bit size
struct name (64) {
...
};
// byte size
struct name (8) {
...
};
Types:
- In C/C++ any integral type (
char
,int
,short
, …) is allowed to be the type of a bitfield. This disallowed structures and classes which make sense. - Another restriction (in strict more) is that the number of bits cannot be more than the size of the type, which makes sense but only insofar as the holding type should be able to hold it.
- I would say that whether it is
signed
orunsigned
is more important and the maximum number of bits should be the max size that the architecture and language can handle (64 or 128 on some systems). By this reason I think that onlysigned
andunsigned
should be used (or whateveri
stands for ini32
).
Mixing:
- Should non-bit fields be allowed to be mixed with bit fields within the same structure. I would argue that this should not be allowed since they for different sorts of purposes.
Usability
Casting:
- Should casting between bit field structures be allowed?
- I would say yes on the condition that the size of the two structures are the same. This is because sometimes two different layouts are used depending on some field within the structure. This obviously cannot be done statically then.
Setting fields:
- What should happen if a value that is unrepresented in a bit field is assigned to it at runtime (one that cannot be determined statically)?
- Several options: panic (very strange, rust doesn’t do this), bound (meaning that it is set to the max value), masked (ignore bits that cannot be represented, this has problems with signed numbers), Result<Err, ()> (have the assignment return a result which can be bubbled up), fail and do nothing.
- I would say that bound is the best choice because of ergonomic considerations.
References and Pointers:
- Like in C/C++ I don’t think that (at least at first) references should be able to be taken for bitfields since there size is not uniform with other types.
Final Exmple:
struct Bitfield(64) {
present : unsigned as 0,
page_size : unsigned as 3..=5,
address : unsigned as 16..=46,
other : signed as 60..=63
}