Replies: 1 comment 17 replies
-
Now that I look back at the part about discriminant, I think I over-thought it. The fact that |
Beta Was this translation helpful? Give feedback.
17 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
-
The goal of this discussion is to design reflection on
enum
types. Ideally,enum
s would at least support every case that the other types currently support.Nomenclature
I will try to use the same names as in
serde
.Each variant is identified by a unique number called its discriminant. Discriminants are not indices, they may not be continuous and can even be set explicitly if all variants are unit variants (that is, in C-like
enum
s).Features
Let's start with a wish-list of features:
TestEnum::A
), and its discriminantenum
s with explicit discriminantsReflect::reflect_ref()
#[derive(Reflect)]
Default
if supported)DynamicEnum
sDoes this look complete/reasonable?
MVP
I propose as a first step to implement features 1 to 4. This means
enum
s could manually implement theEnum
trait to get basic reflection. The other features can be built on it later, which gives us time tobikeshed ondesign them. I will present a possible API and associated design questions.Enum
traitHere is a first shot at the main trait that is the equivalent of the
Struct
trait.Is it missing any feature? Can you think of better names?
EnumVariant
This is an
enum
that contains and describes the current variant. Following the Serde data model, it defines 4 types of variants. It is conceptually similar toenum ReflectRef
forReflect
.An alternative would be to not define
NewType
and consider them as tuples of 1 element. It's unclear to me why serde makes the difference, the Rust Reference does not.Discriminant
This is the tricky part. It's possible to get the discriminant of any variant with
std::mem::discriminant(&TestEnum::A)
. It returns an opaque type:std::mem::Discriminant<TestEnum>
. Since ourDiscriminant
is an associated type, each implementation can set it to the correctstd::mem::Discriminant<T>
. That is enough for addressing since it implementsEq
andHash
but in order to give an actual integer value (e.g. for serialization), we would like to convert that to anusize
(enum
s can never have a size larger thanusize
). For that we need to transmute it, however we have to avoid reading any uninitialized memory. A simple implementation of that looks like this:There is probably a better way to do it, do tell me if you know it. Also, the compiler may be smart enough to optimize everything.
Other note: I used
usize
but the real discriminant's type might be signed. As far as I can tell there is no way to know fromstd::mem::Discriminant<>
since it is opaque, but it can only happen in C-likeenum
s with the#[repr(...)]
attribute and/or explicit discriminants, so it is possible to detect it in the derive macro. Then, we would have a second associated type that is eitherusize
orisize
, and transmute the discriminant to that type.Conclusion
This MVP seems to be implementable quickly when agreed on. I have not seriously investigated how to implement the other features, but I don't foresee any trouble. I will probably wait until we agree on this first part before detailing them here.
Beta Was this translation helpful? Give feedback.
All reactions