The release notes mention my RFC, but a huge thanks also to Vadim Petrochenkov for the implementation, and all the myriad RFC contributors.
The release notes mention my RFC, but a huge thanks also to Vadim Petrochenkov for the implementation, and all the myriad RFC contributors.
In terms of things you can express now but that could still use some work: anonymous structs and unions. You currently still have to name all the intermediate aggregates, even when the C API doesn't. See https://internals.rust-lang.org/t/pre-rfc-anonymous-struct-a... .
myregister.tx_baudrate_bitfield = 1; // Writes the whole myregister
myregister.tx_now_bitfield = 1; // Reads the whole myregister, then bitmasks and finally writes.
The same mistake can of course be done with manual masking also but then it's more obvious that you are reading the registers also. When the code looks like above it's very easy to not realize that it also requires a read.A kind of similar issue appears even with enums, because it is hard to guess the size of the integer that a given enum is implemented with. This is why it some not to use C enums in public interfaces to libraries.
But yes, I can imagine that for Mozilla there is a need to mix Rust and C++ right inside an application at boundaries that are not usually considered external, in which case all kinds of non-portable C constructs are acceptable.
True; fortunately, Rust has a means of declaring the size of an enum, with #[repr(u8)] and similar. But you do have to figure out the size of the C enum.
struct foo {
int num_elements;
char data[0];
}
struct foo *d = malloc(sizeof(foo) + num);
d->num_elements = num;
/* Now d->data is effectively an array char[num_elements] */The way custom DSTs work in Rust is super annoying at the moment, but slightly less annoying for generics.
You are free to define a custom DST that is like:
struct Foo {
header: u32,
flexible: [SomeType]
}
However, there is no way to construct this type. The most you can do is calculate field offsets and write a ton of unsafe code.If the type was instead
struct Foo<T: ?Sized> {
header: u32,
flexible: T
}
you would be able to construct a `&Foo<[SomeType]>` from a `&Foo<[SomeType; N]>` via DST coercions.This only works if you know the size of the array at compile time.
For a runtime sized thing you have to implement a bespoke vector-like thing. You can find an example of that code in https://github.com/servo/servo/blob/e19fefcb474ea6593a684a1c... where we have a generic "HeaderWithSlice<H, [T]>" type which can be heap allocated as a header followed by the flexible DST [T].
This could be improved. The HeaderWithSlice thing could probably be a useful crate for implementing completely-heap-allocated vectors (where the len/cap are on the heap) or shared reference counted types with flexible members. We haven't really split it out as a crate because we don't actually ever mutate it so the amount of code we need is significantly less (but it's not as useful as it could be).
So yeah, flexible array members can be implemented in Rust, but it's a lot of hacky work. It's an equivalent amount of work and unsafety to write a custom Index impl on a repr(C) type with a zero-length array at the end. The DST doesn't actually get you much here.
For example-- what happens if I create an array of foos and then malloc data of each element to some arbitrary size?
I read that they're essentially the same as C unions (~untagged enums), but I have no clue how `match` works in that case.