Although I love Rust's enums (and the idea of making illegal states unrepresentable), I'm struggling to represent (ironic, yes) them in databases like PostgreSQL when the variants have data associated with them.
Example problem
Suppose we have something like this:
enum PetKind {
Dog,
Cat,
Bird { can_fly: bool },
}
struct Pet {
name: String,
kind: PetKind,
}
From what I can tell, traditional databases simply don't support sum types.
This makes it difficult to not only store rich enums (like PetKind) but also query them.
Imperfect solutions I found
Serialize to JSON
For example, we could serialize
let my_pet_kind = PetKind::Bird { can_fly: true };
as
{
"Bird": {
"can_fly": true
}
}
and store it in a column of table pets.
This might work, but with this we probably sacrifice some of the benefits that a relational database offers.
Nullable columns with check constraints
Alternatively, we might have something like this, where data associated with variants comes in the form of nullable columns, while check constraints ensure data consistency:
CREATE TYPE pet_kind AS ENUM ('dog', 'cat', 'bird');
CREATE TABLE pets (
id UUID PRIMARY KEY,
name VARCHAR(255) NOT NULL,
kind pet_kind NOT NULL,
can_fly BOOLEAN,
);
ALTER TABLE pets ADD CHECK (
(kind = 'dog' AND can_fly IS NULL)
OR
(kind = 'cat' AND can_fly IS NULL)
OR
(kind = 'bird' AND can_fly IS NOT NULL)
);
However, the querying of data in this schema in Rust seems very awkward. E.g., one may need to define intermediate data types in Rust, like
enum PetKindSql {
Dog,
Cat,
Bird,
}
struct PetSql {
name: String,
kind: PetKindSql,
can_fly: Option<bool>,
}
and convert them to the proper ones later.
Question
Is this the best we can do? Is there a way to avoid serializing enums to JSON while preserving easy retrieval of data in Rust?
I've searched through a lot of pages—and maybe I'm searching for the wrong things—but I just can't find an elegant and robust solution. But maybe that's simply because conventional databases favour product types over sum types? If so, is anyone working on the latter?