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
RequestV1andRequestV2where theendfield is only present inRequestV2. - An enum
Requestwith variantsV1andV2wrappingRequestV1andRequestV2respectively. - A getter function on
Requestfor the sharedstartfield, 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
Optionfield. In our example,Requestcould defineend: Option<u16>. - If you can achieve backwards compatible (de)serialization through clever use of
serdemacros.
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 withCopytypes, etc.