in doing so, i learned a couple of Rust tricks that i wanted to share.
base classes? what are you talking about, dma? rust doesn’t have base classes!
unfortunately, someone forgot to tell the activity
stream spec writers that when they
defined the spec as it explicitly requires inheritance between types. for
IntransitiveActivity type inherits everything from the
type. we can capture this in rust with:
note the serde
flatten directive that means when we
serialize an instance of
IntransitiveActivity it will hide this indirection
Acitivity’s members in the resulting JSON.
however, we still have an issue that all users of the type need to be aware of
base field and reference
Acitivity’s members through that. this makes
for a clumsy API. one more trick we can use is to implement
Deref for the
this essentially treats
IntransitiveActivity as if it were a pointer type and
allows direct access to the members of
Activity as if
base weren’t there.
Deref documentation points out that this “should only be implemented for
smart pointers to avoid confusion” as it is also used implicitly by the compiler
in some circumstances, and the rules under which it will do so are specifically
aimed at smart pointers. however, i haven’t found an alternative approach or
noticed any issues, so i’m sticking with it.
for convenience i defined a
provides default implementations of json serialization and deserialization.
this requires plumbing through of the
lifetime to ensure that any deserialized
objects respect the incoming stream’s lifetime. where this becomes tricky is
when a struct that derives from
Deserialize has its own lifetime bound, for
the tl;dr magic invocation is:
which ensures that
'a is bound by
one other consideration is that some types cannot have their lifetimes implicitly managed by serde1. of course, we can always just copy the data and avoid the issue, but it is nicer to avoid this where possible2.
the magic parameter is
#[serde(borrow)] applied to any field that needs it.