-
Notifications
You must be signed in to change notification settings - Fork 1
Description
A much harder bit but potentially very "lucrative".
A "normal" enum would be derived SoAble somehow like this:
#[repr(u8)]
#[derive(SoAble)]
enum Value<'a> {
Undefined = 1,
Null,
Boolean(bool),
String(StringIndex<'a>),
SmallString(SmallString),
}
union ValueField0<'a> {
undefined: (),
null: (),
boolean: bool,
string: StringIndex<'a>,
small_string: SmallString,
}
struct ValueRef<'soa, 'a> {
discriminant: &'soa Discriminant<Value>, // this is an actual type in Rust: https://doc.rust-lang.org/core/mem/struct.Discriminant.html
_0: &'soa ValueField<'a>
}ie. We create as many _N unions and enums as there are variants, with each variant getting its own union field. Note that this is a bit dangerous (and not just because of the unions) as a user might have a single variant which as many, many fields and we'd then generate a whole bunch of fields for that. That's kind of "user error" at that point though: your enum should be reasonably SoAble to make the cut.
Note also that we do use &'soa Discriminant<Value> instead of reading the Discriminant out; while it'd be pretty cool to automatically create an enum ValueRef which reuses the discriminants of Value and just contains a reference in each field, it would be impossible to make that work for ValueSlice and furthermore sometimes you don't actually care to read the discriminant. See below.
A "named" enum is an interesting example as well:
struct TokenIndex(u32);
struct NodeIndex(u32);
struct ExtraIndex(u32);
struct OptionalTokenIndex(Option<NonNullU32>);
struct OptionalNodeIndex(Option<NonNullU32>);
struct OptionalTokenAndNode {
token: OptionalTokenIndex,
node: NodeIndex,
}
struct ExtraAndOptionalNode {
extra: ExtraIndex,
node: OptionalNodeIndex,
}
#[derive(SoAble)]
enum Node {
Root {
main_token: TokenIndex,
},
TestDecl {
main_token: TokenIndex,
data: OptionalTokenAndNode,
},
GlobalVarDecl {
main_token: TokenIndex,
data: ExtraAndOptionalNode,
}
}So this would be a collection of enum variants where each variant has at least main_token (1-byte) and then possibly a data field (2x 4-byte). This would derive SoAble like this:
union NodeData {
root: TokenIndex,
test_decl: OptionalTokenAndNode,
global_var_decl: ExtraAndOptionalNode,
}
struct NodeRef<'soa> {
discriminant: &'soa Discriminant<Node>,
main_token: &'soa TokenIndex, // note: not behind a union because all fields agree on the type!
data: &'soa NodeData,
}This Node example is the first few lines of Zig's AST which is pretty great, but in the SoAble derive format it gets kind of even better, or at least it has the potential to become really great. Maybe it would be better to copy the discriminants into a separate NodeDiscriminant type so that the variants could be named? Or maybe not, time will tell.
One thing that is sure is that it'll be pretty easy to write safe APIs on top of this, as access into noderef.data union's fields will be fairly self-explanatory: if you match on discriminant == GlobalVarDecl then you'll access noderef.data.global_var_decl no problem.