So, in previous posts, I discussed the pros and cons of two different approaches to modeling variants: Rust-style enums and C++-style classes. In those posts, I explained why I see Rust enums and OO-style class hierarchies as more alike than different (I personally credit Scala for opening my eyes to this, though I’m sure it’s been understood by others for much longer). The key points were as follows:
- Both Rust-style enums and C++-style classes can be used to model the
idea of a value that be one of many variants, but there are
differences in how they work at runtime. These differences mean that
Rust-style enums are more convenient for some tasks, and C++-style
classes for others. In particular:
- A Rust-style enum is sized as large as the largest variant. This is
great because you can lay them out flat in another data structure
without requiring any allocation. You can also easily change from
one variant to another. One downside of Rust enums is that you cannot
refinethem to narrow the set of variants that a particular value can have.
- A C++-style class is sized to be exactly as big as one variant. This is great because it can be much more memory efficient. However, if you don’t know what variant you have, you must manipulate the value by pointer, so it tends to require more allocation. It is also impossible to change from one variant to another. Class hierarchies also give you a simple, easily understood kind of refinement, and the ability to have common fields that are shared between variants.
- A Rust-style enum is sized as large as the largest variant. This is great because you can lay them out flat in another data structure without requiring any allocation. You can also easily change from one variant to another. One downside of Rust enums is that you cannot
- C++-style classes offer constructors, which allows for more abstraction and code reuse when initially creating an instance, but raise thorny questions about the type of a value under construction; Rust structs and enums are always built in a single-shot today, which is simpler and safer but doesn’t compose as well.
What I want to talk about in this post is a proposal (or proto-proposal) for bridging those two worlds in Rust. I’m going to focus on data layout in this post. I’ll defer virtual methods for another post (or perhaps an RFC). Spoiler alert: they can be viewed as a special case of specialization.
I had originally intended to publish this post a few days after the
others. Obviously, I got delayed. Sorry about that! Things have been
very busy! In any case, better late than never, as
some-great-relative-or-other always (no doubt) said. Truth is, I
really miss blogging regularly, so I’m going to make an effort to
write up more
in progress and half-baked ideas (yeah yeah, promises
to blog more are a dime a dozen, I know).
Note: I want to be clear that the designs in this blog post are not
my work per se. Some of the ideas originated with me, but others
have arisen in the course of conversations with others, as well as
earlier proposals from nrc, which in turn were heavily based on
community feedback. And of course it’s not like we Rust folk invented
OO or algebraic data types or anything in the first place. :)