Boilerplate-free Struct Transforms in Rust.
The last several posts have introduced a number of abstractions, namely HList, Generic, LabelledGeneric, as well as pluck()
and sculpt()
. Although each of those have impressive party tricks of their own, I’d like to share how you can use them to write a reuseable, generic function that handles converting between structs with mis-matched fields and thus have different LabelledGeneric
representations.
Unlike the last post, this one will be relatively light on recursion and mind-bending type-level stuff; it’s time to sit back and enjoy the fruits of our labour.
Adding Frunk to your project
Much of this post will make use of Frunk’s types (e.g. HCons
, HNil
), methods, macros (esp. for describing macro types via the Hlist!
type macro), and terminology.
It might be easier to follow along if you add Frunk to your project and play around with it. Frunk is published to Crates.io, so to add it your list of dependencies, simply put this in your Cargo.toml
:
1 2 |
|
Alternatively, take a look at the published Rustdocs.
Boilerplate-free conversions between Structs
Suppose we have a bunch of structs that are similar-ish in terms of their data but ultimately, not necessarily
exactly the same. This means we can’t just use the normal LabelledGeneric
convert_from
method to convert between them.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
|
In our example, PresentableUser
and InternalApiUser
structs have fields that are subsets of the fields in UserFromDb
, and not in the same order either. The scenario is that UserFromDb
is a struct that we get from reading our persistence layer, and the other 2 are types that we use in our application for business logic.
Assuming a flow where we want to be able to go from UserFromDb
to either PresentableUser
or InternalApiUser
, the idea is that we don’t want be holding on to sensitive data like pw_hash
when we don’t need to, thus lowering the risk of accidentally leaking said data (e.g. serialising it by accident, or by rendering it in debug messages, etc).
While we could go about writing From
s by hand for each of these, and for every other time a similar situation arises, that’s quite a lot of boilerplate to write and maintain. Thankfully, we can make use of Frunk’s LabelledGeneric
and Sculptor
to write a single, reuseable generic function.
Note, for a review of:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
Not bad. The body of the function is literally 3 lines long :) Now we can do this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
In actuality, Frunk already ships with this function so you can use it out of the box.
Conclusion
Often times, you’ll hear that heterogeneous lists enable developers to write reuseable generic functions because they abstract over arity and types, and it might not be obvious exactly what that means on a practical level. The example shown in this post just scratches the surface of what is made possible through HList
and LabelledGeneric
, and there are definitely more creative usages out there, such as building of boilerplate-free (e.g. JSON) codecs (hint: look to Haskell and Scala libs for more).
As usual, please give it a spin and chime in with any questions, corrections, and suggestions !