Introduction
SuperStruct is a Rust library for working with versioned data. It allows you to define and operate
on variants of a struct
which share some fields in common.
As an example, imagine you're working on a program that accepts a Request
struct from the user.
In the first version of the program you only allow users to specify a start: u16
field:
#![allow(unused)] fn main() { pub struct Request { start: u16, } }
After a while you realise that it would be nice if users could also specify an end: u16
in their
requests, so you would like to change the definition of Request
to:
#![allow(unused)] fn main() { pub struct Request { start: u16, end: u16, } }
Now imagine that your program needs to work with old versions of Request
as well as new, i.e.
it needs to be backwards-compatible. This is reasonably common when databases are involved and
you need to write schema migrations, or when working with network protocols.
SuperStruct allows you to define both versions of the Request
with a single definition, and
also generates an enum to unify them:
use superstruct::superstruct;
#[superstruct(variants(V1, V2))]
pub struct Request {
pub start: u16,
#[superstruct(only(V2))]
pub end: u16,
}
#[cfg_attr(test, test)]
fn main() {
let r1 = Request::V1(RequestV1 { start: 0 });
let r2 = Request::V2(RequestV2 { start: 0, end: 10 });
assert_eq!(r1.start(), r2.start());
assert_eq!(r1.end(), Err(()));
assert_eq!(r2.end(), Ok(&10));
}
The superstruct
definition generates:
- Two structs
RequestV1
andRequestV2
where theend
field is only present inRequestV2
. - An enum
Request
with variantsV1
andV2
wrappingRequestV1
andRequestV2
respectively. - A getter function on
Request
for the sharedstart
field, e.g.r1.start()
. - A partial getter function returning
Result<&u16, ()>
forend
, e.g.r2.end()
. - Lots of other useful goodies that are covered in the Codegen section of the book.
When should you use SuperStruct?
- If you want to avoid duplication when defining multiple related structs.
- If you are considering manually writing getters to extract common fields from an enum.
- If you are considering writing traits to unify types with fields in common.
When should you not use SuperStruct?
- If you can get away with just using an
Option
field. In our example,Request
could defineend: Option<u16>
. - If you can achieve backwards compatible (de)serialization through clever use of
serde
macros.
What next?
- Check out the Code Generation docs.
- Check out the Configuration docs for information on how to
control
superstruct
's behaviour, including renaming getters, working withCopy
types, etc.