I'd argue that most research on type theory has nothing to say about ABI, because most languages don't support ABI stability at all. In most languages, if you want to combine two pieces of code, you're just expected to feed them into the same compiler at the same time.
You might counter that some languages that don't talk about ABI still have concepts of open-world polymorphism that could be made to fit. But they would be a poor fit. The thing about this use case is that it's not really open-world. It's not as if any crate can define its own enum variants. There is still a single authoritative definition; it's just that @InfernoDeity wants to be able to extend the definition without breaking compatibility with client crates. If we were only talking about the API level – if "breaking compatibility" meant "requiring a code change" – then that would be exactly the purpose of #[non_exhaustive]
. Instead we're talking about ABI, where "breaking compatibility" means "requiring a code change or rebuild". But that doesn't make it fundamentally different from a type theory perspective.
More concretely, dyn Trait
is not a good fit. For one thing, it adds an extra indirection. But suppose we fixed that by coming up with some variant of dyn
where the vtable pointer is part of the object itself, something that, as you probably know, has been requested in the past for other use cases. There would still be the problem that a vtable pointer is much larger than a 1- or 2-byte discriminant.
Even if efficiency weren't a concern, what would the trait even do? The desired use case is for clients to match against known variants while ignoring unknown ones. I guess one could use the visitor pattern – some convoluted thing like:
trait Foo {
fn visit(&mut self, visitor: &mut dyn FooVisitor);
}
trait FooVisitor {
// instead of A { bar: i32, baz: i64 }
fn visit_variant_a(&mut self, bar: i32, baz: i64);
// instead of B { bay: String }
fn visit_variant_b(&mut self, bay: String);
}
But this is just like how people emulate sum types in Java or whatever – languages that have class-based polymorphism but don't support true sum types. It would be a depressing sight in Rust, which does support them.
Not to mention that traits aren't natively ABI-stable either, though they can be emulated.
Sidenote: I have a personal stake in this. I have a fairly ambitious project in mind for the future, which I would like to write in pure Rust. However, for my use case it will be critical for most boundaries between different crates to be ABI-stable. I even want to write an ABI-stable wrapper for a significant chunk of std
and core
.
My current plan is to do this with the abi_stable
crate (or perhaps implement something similar myself), which essentially reimplements some language features inside procedural macros, to work around the fact that the real versions of the features don't support ABI stability. For example, there's no option to make trait objects ABI-stable, so abi_stable
has an attribute macro you can apply to traits to creates a custom trait object, a repr(C)
struct full of extern "C"
function pointers, and generate the necessary plumbing between that and the actual trait. It also supports non-exhaustive enums!
But as you might expect, these workarounds have significant limitations and ergonomic penalties. So I would like the language to offer native ABI stability as an option for as many features as possible.