0

By saying LLVM-like inheritance hierarchy, I mean the way to obtain runtime polymorphism described in this documentation: https://llvm.org/docs/HowToSetUpLLVMStyleRTTI.html.

It is easy to implement the same feature in Rust using enums, like:

enum Shape {
  Square(Square),
  Circle(Circle)
}

enum Square {
  SquareA(SquareAData),
  SquareB(SquareBData),
}

enum Circle {
  CircleA(CircleAData),
  CircleB(CircleBData),
}
// assume ***Data can be arbitrarily complex

However, the memory layout is inevitably different from LLVM-like inheritance hierarchy, which uses a single integer field to record the discriminant of the type. Though current rustc already has a lot of optimizations on the size of enums, there will still be two integer field to record the discriminant in a Shape object in the above example.

I have tried some ways without success, in which the closet to LLVM-like inheritance hierarchy in my mind is to enable nightly feature arbitrary_enum_discriminant and assign each variant of the enum an discriminant:

#![feature(arbitrary_enum_discriminant)]
enum Shape {
  Square(Square),
  Circle(Circle)
}

#[repr(usize)]
enum Square {
  SquareA(SquareAData) = 0,
  SquareB(SquareBData) = 1,
}

#[repr(usize)]
enum Circle {
  CircleA(CircleAData) = 2,
  CircleB(CircleBData) = 3,
}

It is perfectly possible for Shape to go without its own discriminant, since its two variants have non-intersecting discriminant sets. However, rustc still assigns a integer discriminant to it, making it larger than Square or Circle. (rustc version: rustc 1.44.0-nightly (f509b26a7 2020-03-18))

So my question is: is it on earth possible in Rust to use enums to model LLVM-like inheritance hierarchy, with only a single integer discriminant in the top level "class"?

MashPlant
  • 81
  • 3
  • 2
    I think this is answered by [Why does Rust use two bytes to represent this enum when only one is necessary?](https://stackoverflow.com/questions/54499156/why-does-rust-use-two-bytes-to-represent-this-enum-when-only-one-is-necessary) – trent Apr 10 '20 at 02:12
  • 1
    The quick version: You can't merge the discriminants because doing so would make it impossible to borrow the `Square` from a `Shape::Square` variant (it would have a different layout when inside `Shape` than outside). – trent Apr 10 '20 at 02:14
  • 1
    @trentcl Not the same thing: in the OP's case, he has ensured that the child discriminants are non-intersecting, so the child layout wouldn't change between inside and outside. – Jmb Apr 10 '20 at 06:31
  • My bad. In that case I think it is instead a duplicate of [Why is this Rust enum not smaller?](https://stackoverflow.com/q/57326734/3650362) The niche-filling optimization isn't yet smart enough; it only knows how to handle enums with exactly one nonempty variant. I tried to improve this and I wasn't clever enough; if you think you can do better, the linked issue is a good place to start. – trent Apr 10 '20 at 11:56
  • Does this answer your question? [Why is this Rust enum not smaller?](https://stackoverflow.com/questions/57326734/why-is-this-rust-enum-not-smaller) – Jmb Apr 14 '20 at 06:51
  • 1
    Agree. See in particular [this comment](https://github.com/rust-lang/rust/issues/46213#issuecomment-415807521) in the linked issue. – Jmb Apr 14 '20 at 06:52

0 Answers0