►
Description
Armin is going to tell us about his experiences with Serde, the popular Serialization/Deserialization crate. Serde is format-independent and lets you work with JSON, YAML, and a range of different formats. Despite all of this, there is a lot that can be accomplished with it and some of the use cases are quite interesting and worth sharing.
Armin's blog: https://lucumr.pocoo.org/2021/11/14/abusing-serde/
Minijinja: https://github.com/mitsuhiko/minijinja
Armin on Twitter: https://twitter.com/mitsuhiko
Rust Linz on Twitter: https://twitter.com/rustlinz
Speak at Rust Linz: https://sessionize.com/rustlinz
A
I
want
to
talk
a
little
bit
about
30,
which
is
one
of
my
favorite
libraries,
but
before
I
go
to
that,
I
want
to
explain
a
little
bit
about
why
I'm
using
this
in
the
first
place.
So
I
work
for
century,
which
you
might
know
it's
a
crash
reporting
company
and
I
have
secretly
introduced
rust
into
as
century
a
long
time
ago.
A
I
think
about
2015
when
we
first
started
using
pipe
modules
extended
from
rust
code,
so
we
wrote
some
stuff
in
rust
and
then
sort
of
used
from
the
rest
of
the
pipe
and
ecosystem,
and
we
built
there
a
lot
of
the
ingestion
pipeline
in
rust
and
one
of
the
things
that
you
do
a
lot
there
is
serializing
and
serializing.
A
So
evidently
we
use
a
lot
of
sturdy
30,
but
we
also
have
sort
of
a
problem
there
because
like
when
we
started
using
rust.
That
was
maybe
not
the
best
ecosystem
around
for
some
of
the
problems
that
you
would
typically
have
particularly
testing,
and
so
one
of
the
things
that
I
wrote
specifically
to
address
some
problems.
There
is
this
library
called
insta,
which
is
a
snapshot
testing
tool,
and
it
also
internally
uses
sorry
and
so
some
of
the
stuff
that's
going
on
in
this
talk.
A
Is
it's
not
directly
coming
out
of
insta,
but
this
is
this
is
where
a
lot
of
this
sort
of
originally
took
place.
So
some
of
the
code
that
now
lives
in
other
libraries
and
that
I
will
actually
go
through
in
this
talk-
started
out
really
in
this
in
this
insta
library,
and
so,
if
you're
into
snapshot,
testing
and
rust.
You
should
look
at
this
because
I
think
it's
quite
a
cool
cool
agreement
that
you
can
actually
use
to
to
to
do
some
interesting
test
cases,
okay,
so
about
30..
A
So
the
talk
is
called
surly
shenanigans
and
it's
basically
about
like
using
this
in
ways.
It's
a
little
bit
unorthodox.
I
also
wrote
a
blog
post
about
this
recently,
which
goes
into
very
much
the
same,
but
I
want
to
give
a
little
bit
more
flavor.
A
So
30
is
a
serialization
deserialization
framework
and
it's
a
little
bit
similar
to,
for
instance,
I'm
not
sure
if
it's
similar
to
a
lot
of
systems
that
exist,
but
it
abstracts
away
some
of
the
ways
in
which
you
reason
about
serialization
deserialization,
and
so
it
exists
since
around
the
first
time
compiler
plugins
became
available
in
rust,
and
so
it
actually
has
quite
a
bit
of
history,
and
it's
it's
quite
flawed.
A
I
would
say
it's
awesome,
but
there
are
a
lot
of
restrictions
in
there
which
were
okay
at
the
time,
but
I
think
by
now
we
have
figured
out
that
maybe
maybe
some
of
it
needs
some
improvements,
but
despite
all
of
those
limitations,
there's
actually
still
a
lot
you
can
do.
But
before
I
want
to
go
into
sort
of
ways
in
which
you
can
abuse
this,
I
want
to
sort
of
quickly
go
into
how
it
works
at
all.
A
If
you
haven't
used
it
yet
get
to
parts
30
serialization,
these
organizations
start
with
the
first
one.
There
are
two
kind
of
important
traits.
The
first
one
is
called
serialize,
and
this
trait
basically
defines
how
a
type
wants
to
be
serialized
and
there's
a
second
trait,
which
is
called
serializer,
which
is
basically
a
trait
which
is
implemented
by
a
thing
that
knows
how
to
serialize
a
serialized
object.
So,
for
instance,
a
serializer
would
say
like.
I
know
how
to
write
json.
I
know
how
to
write
yaml,
where
serialized
knows.
A
I
know
how
to
instruct
the
serializer
to
basically
serialize
myself.
So
in
this
very
simple
case,
you
have
a
point
with
two
members
x
and
y,
and
you
can
basically
implement
serialize
for
point.
Serialize
is
a
pretty
basic
trade,
it
doesn't
have
any
lifetimes
or
something
associated
has
one
method
on
it
called
serialize,
which
takes
a
parameterized
sterilizer,
and
it
can
do
some
stuff
on
it.
A
What's
important
here-
and
this
is,
I
think,
actually
quite
clever-
is
that
the
serializer
moves
in
which
means
that
the
the
serializer
is
moved
and
consumed
in
the
serialize
method,
which
means
that
you
can
actually
hold
quite
a
bit
of
state
on
the
serializer
which
then
at
the
end
of
it.
It
can
move
out.
So
in
this
case,
it's
not
evident
what
this
does,
but
I'll
show
you
an
example
later
of
why
this
is
quite
valuable.
A
But
here
you
can
actually
see
that
the
serializer
has
instructed
serialize
a
struct
of
called
point.
It
has
two
members
and
what
the
serializer
returns
in
this
case
is
it's
kind
of
like
a
a
struct
serializer
substate
object,
which
you
then
kind
of
say
like
hey.
I
want
it's
field
serialized,
another
field,
serialized
and
then
I
want
to
end
the
whole
thing
where
I
am
and
the
end
method
then
returns.
The
end
result
that
the
serializer
things
should
be
returned.
A
Most
serializers,
don't
return
anything
so
they
will
return
void,
but
you
can
actually,
you
can
actually
have
the
serializer
return,
some
interesting
data-
and
I
will
take
advantage
of
this
later
because
nobody
really
wants
to
write
code
like
this
there's
automatic
support
to
automatically
deriving
this.
This
pretty
much
generates
the
same
thing
as
before,
but
it's
actually
quite
a
bit
of
stuff.
You
can
do
on
this
sort
of
automatic
deriving
system
to
customize
this
a
little
bit.
A
So
I
already
mentioned
that
consuming
serialis
is
a
really
useful
property
and
the
reason
this
is
a
useful
property
is
because
you
don't
have
to
produce
bytes.
A
lot
of
serializers
will
produce
bytes,
in
particular
everything
that
sort
of
is
a
wire
format
like
yaml
json,
even
bin
codes.
A
They
will
typically
take
a
writer
and
sort
of
the
input
to
the
serializer
and
the
serializer
will
hold
the
writer
and
then
every
class
method
will
just
write
into
the
writer,
but
you
can
also
do
more
interesting
things,
so
here,
for
instance,
this
is
straight
out
of
originally
insta,
but
this
is
also
what
I
use
in
mini
ginger,
which
is
a
serializer.
It's
got
a
value
serializer,
and
it
actually
produces
an
object.
A
So,
instead
of
serializing
into
bytes,
it
returns
a
new
value.
In
this
case,
it's
like
a
value
type.
It's
called
values,
just
an
enum
of
a
bunch
of
different
variants.
In
this
case
you
can
see
like
if
it
tries
to
serialize
an
I-64.
It
actually
returns
a
value
that
has
an
internal
representation
of
I-64..
A
Now,
why
is
this
useful?
But
it's
useful
because
it
can
transform
serializable
things
into
different
things,
so,
for
instance,
I
don't
want
to
like
go
too
much
into
this,
because
it's
kind
of
hard
to
see-
but
here
you
can
basically
see
like
this-
is
this
is
still
the
same
serializer
from
a
value
serializer
and
when
you,
when,
when
it
wants
to
serialize
a
struct,
so
a
type
comes
around.
This
is
like
hey,
I'm
a
struct
like
I'm
the
point
of
two
x
and
y
coordinates.
A
A
Well,
first,
it
will
produce
this,
so
it
will
say
like
hey.
I
have.
I
want
to
collect
a
bunch
of
fields
into
this,
and
so
then
this
here,
like
struct
implementation,
which
is
this
one
here
whenever
serialized
field
is
invoked,
which
is
what
we
used
earlier.
So
you
will
see
this
here
right,
so
serialized
truck
returns,
the
state
the
state
is
actually
this
thing
that
we
have
over
there
and
then
serialize
field
is
being
invoked
right
and
so.
A
This
is
what
we
have
here,
so
we
actually
say
like
okay,
I
want
to
store
the
key
and
then
the
value
which
is
again
a
serializable
will
again
go
back
into
the
value,
serialize
and
sort
of
effectively
descend
into
like
recursively
into
the
next
kind
of
thing,
and
then
we
end
this
by
producing
a
new
value
which
is
now
represented
as
a
struct
of
all
the
fields
all
right.
So
this
is
busy
an
example
of
how
a
serializer
doesn't
actually
have
to
create
bytes.
It
can
actually
create
a
whole
bunch
of
other
interesting
things.
A
The
next
part
after
this
is
oh
and
what's
important.
To
notice
here
again
is
because
this
consumes
so
the
the
end
of
the
the
end
method
on
on
this
consumes.
It
means
that
you
can
move
out
the
fields
from
from
from
the
serialized
structure.
A
If,
if
it
wouldn't
be
able
to
consume,
it
would
just
be
and
mute
self.
You
would
have
to
do
some
other
stuff,
like
maybe
move
out
of
an
option,
but
this
is
quite
convenient.
This
way,
deserialization
is
a
lot
more
complicated
for
two
reasons.
A
The
first
one
is
that
the
different
formats
in
xero
in
30.,
so
I
think
everybody
can
quickly
understand
how
realizing
works.
It's
just
you
recursive
structure
and
you
just
write
some
stuff,
but
in
deserialization
there
are
different
types
in
which
you
can
deserialize
their
formats
that
are
like
json,
where
it's
pretty
clear.
What's
coming
your
way,
but
then
there
are
formats
that
are
what's
called
non-self
describing.
So
these
are
formats
that
don't
tell
you
what
data
is
coming.
A
I
just
want
to
go
into
the
first
one,
which
is
the
the
one
that
involves
self
self
describing
inputs.
So
self-describing
format
is
basically
a
deserializer
that,
whenever
it
parses
a
token,
can
determine
from
the
token
what
it
is.
I
can
json,
for
instance,
if
a
string,
if
they're
double
quotes,
then
you
know
it's
going
to
be
a
string.
If
the.
If
it
starts
with
the
serum
number,
then
you
know
it's
going
to
be
a
number.
A
A
And
we
implement
two
variants
to
it.
The
first
one
is
a
u64
variant.
So
if
the
d
serialize
is
invoked
with
u64,
it
will
directly
create
timestamp.
If
it's
passed
string
from
the
deserializer,
then
it
will
try
to
parse
it
with
the
chronology
time
parsing
function
and
then
again
do
the
same
and
sort
of
the
trick.
Here.
Is
this
d
serializer
deserialize
any
at
the
end?
This
basically
means
these
serializer.
A
A
The
second
sort
of
more
complicated
part
here
is
that
time
lifetimes
are
involved.
So
one
of
the
things
you
can
do
and
not
a
lot
of
people
do
do
this,
because
it's
complicated
is
that
you
can
deserialize
and
you
can
borrow
out
of
the
deserializer's
input.
A
A
The
pretty
obvious
thing
is
that
the
deserializer
can
only
ever
produce
one
output
type,
which
is
itself
so
the
serialized
trade
can
only
ever
output
itself.
So
in
this
case
the
visitor
can
only
produce
this
one
too,
as
mentioned
earlier,
deserialized
nd
is
not
always
supported.
A
This
is
what
the
three
different
types
of
formats
look
like.
That
certainly
can
roughly
work
with,
so
the
first
is:
what's
called
a
self-describing
and
human
readable
format.
Both
of
those
concepts
are
called
this
way.
Inserty.
I
don't
know
if
they're
generally
terms
that
are
used,
self-describing
means
you
can
take
an
input
like
a
json
document
and
even
without
knowing
anything
about
the
schema,
you
can
make
sense
of
it.
A
The
second
example:
this
is
message
pack,
which
is
also
a
self-describing,
so
it
you
can
translate
this
to
a
chase
man
back
and
it
will
more
or
less
look
the
same,
but
it's
not
human
readable
because
it
obviously
involves
a
bunch
of
binary
stuff
and
the
last
one,
which
is
probably
not
as
easy
to
see,
is
that
this
is
a
non-self-describing
format,
and
this
is
sort
of
a
packed
binary
representation
where
you
have
to
know
that
the
first
thing
you're
going
to
decode
is
key
one,
and
so
the
only
thing
that
the
format
tells
you
is
like.
A
Okay,
the
next
five
bytes
are
value,
just
sort
of
the
values
to
key
one
and
then
the
next
five
bytes
again
are
in
this
case.
Actually
this
format
kind
of
assumes
that
this
is
a
tuple
of
two
items.
So
it
says
again,
there's
going
to
be
a
string
of
five
bytes
called
value
and
then
there's
going
to
be
the
the
one
byte
value
number.
Two
and
obviously
here
the
the
problem
is
that
if
you
don't
know
what
you
just
what
you're
decoding,
then
you
can't
make
any
sense
of
it.
A
So
you
have
to
understand
the
schema
so
that
you
can
parse
this
and
it
serializes
support
in
30
can
support
all
of
those
cases.
A
What
we're
going
to
look
at
in
this
talk
is
actually
only
going
to
be
self-describing.
Non-Self-Describing
formats
in
30
are
supported
well
by
30,
but
they're
not
supported
well
by
the
ecosystem.
A
This
has
also
quite
a
bit
to
do
with
buffering
when
you,
for
instance,
take
json.
You
have
no
guarantee
in
which
order
the
keys
are
coming
in.
So
there's
no
guarantee
that
key
one
comes
before
key2,
so
for
a
deserializer,
it's
actually
better
to
just
parse
the
whole
thing
and
then
rearrange
these
keys
to
sort
of
what's
expected,
rather
than
to
assume
that
the
first
value
is
going
to
come
in
is
going
to
be
key
one
and
for
a
non-self-describing
format.
You
can
actually
assume
perfect
ordering
okay.
A
So
that's
the
boring
part,
let's
see
what
we
can
actually
do
with
this,
so
I
have
two
things
I
want
to
show
the
first
one,
and
both
of
them
are
related
to
sort
of
libraries.
I
wrote
but-
and
I
wasn't
the
first
one-
sort
of
discovered
this
or
used
this,
so
the
first
thing
is:
you
can
use
30
to
implement
runtime
type
systems.
A
This
is
what
I
use
this
for.
I
have
a
rust
re-implementation
of
my
changer
template
engine.
It
doesn't
do
everything
that
the
python
version
does,
but
it
comes
relatively
close.
I
would
say
it's
called
mini
ginger
because
it
only
has
30
as
a
dependency,
so
everything
else
is
entirely
optional.
A
What
it
looks
like
so
you
create
this
environment
object
and
then
you
parse
and
compile
a
template
by
adding
it
to
this
environment,
and
then,
when
you
call
this
context,
method
or
sorry,
the
context
macro
you're
busy,
creating
like
a
an
object
on
the
spot
here,
which
is
just
a
30
serializable
object,
which
basically
says
like
there's
a
key
called
name
and
there's
a
value
called
john
and
then
it
invokes
render
and
what's
important
is
that
I
don't
have
to
have
the
name
john.
A
A
You
can
add
numbers,
you
can
call
methods
you
can
you
can
change
it,
convert
it
between
types,
everything
that
you
would
expect,
and
so
basically,
for
this
templating
engine
internally,
there
has
to
be
a
runtime
representation
of
the
values
because,
like
until
you
run
this,
you
don't
know
what's
in
there
and
for
this
to
work,
I'm
basically
using
30.
A
internally
in
the
evaluator.
This
has
nothing
to
do
with
30,
but
I
think
it's
important
to
understand
in
the
evaluator
you
have
basically
a
bytecode
interpreter,
and
so
every
instruction
is
a
bytecode
instruction
and
based
on
that,
it
does
something.
And
what
you
can
see
here
store
local
is,
for
instance,
the
instruction
to
store
the
top
of
the
stack
in
in
a
variable
of
a
specific
name.
So
in
this
case
it
pops
for
store
local.
A
It
pops
the
top
value
from
the
stack,
so
the
stack
machine,
and
then
it
stores
it
in
the
local
variable
called
name
for
lookup
it
does
the
inverse.
It
tries
to
look
up
a
variable
with
a
specific
name
in
in
in
the
current
context,
and
then,
if
it
doesn't
find
it,
it
returns
undefined
and
then
get
at
her
is
kind
of
similar.
You
take
the
top
value
from
the
stack
and
then
you
look
up
an
attribute
of
her
specific
name
and,
as
you
can
see,
value
dot
get
at.
A
Her
is
sort
of
a
method
that
exists
on
the
on
the
value
trade,
so
as
I
on
the
value
type
so
that
you
can
actually
do
dynamic
lookup.
So
how
does
this
work
in
the
engine?
So
this
is
an
example
from
change
a
template,
so
you
have
a
for
loop,
so
it
goes
for
every
user
in
the
list
of
users
it
wants
to
render
this
and
the
interesting
part
here
is.
A
Actually
I
don't
know
if
anyone's
familiar
with
the
change
the
templating
engine,
but
changer
gives
you
the
secret
variable
called
loop
and
loop
refers
to
the
loop
itself.
So
as
you're
iterating
over
your
your
sequence
or
your
iterable
behind
the
scenes,
the
loop
also
exists,
and
it
gives
you
access
to,
for
instance,
the
current
index
of
the
iteration,
or
it
gives
you
access
to
the
length
of
the
iterator
and
one
of
the
things
that
ginger
also
always
provided.
A
Was
this
loop
at
the
cycle
method,
and
this
is
also
what
I
implemented
here
so
on
the
loop
context,
there's
a
cycle
method
and
it
can
give
it
multiple
parameters
and
so
on.
First
iteration
it
will
return
what
on
second
iteration
vector
and
even
on
the
third
iteration.
It
will
return
odds.
So
it
basically
just
takes
this
input
and
it
does
motor
loop
index.
A
So
what
does
this
have
to
do
with
30?
Well,
what
it
has
to
do
with
30
is
that
this
loop
object
has
to
exist
in
this
runtime
type
system
that
we
have
so
the
input
to
the
engine
is
a
bunch
of
30
compatible
types.
So
what
are
30
compatible
types,
integers
strings
lists,
structs
options,
but
that's
pretty
much
it
tuples
2,
but,
for
instance,
30
doesn't
have
a
concept
of
well
file.
A
So
if
you
find
this
take
the
loop,
so
the
loop
has
state
on
it
like
index
length
and
so
forth,
but
you
cannot
serialize
the
loop.
You
could
only
serialize
the
current
state
of
the
loop,
but
you
can't
pass
the
loop
through
serialized
d
serialize,
but
this
is
actually
what's
necessary
for
this
system
here
to
work
in
the
templating
engine
a
little
bit,
and
I
will
explain
why
later,
but
the
second
part,
that's
kind
of
more
obvious,
is
that
this
is
a
method
on
it
right.
A
So
how
does
this
work
internally
in
the
engine?
It's
again
the
same,
there's
a
method
on
it
like
get
adhered
as
a
methodology
called
call
method,
and
so
you
can
call
a
method
on
a
value.
A
A
It
pushes
the
current
loop
context
on
top
of
the
stack,
and
the
only
thing
that
we
care
about
here
is
actually
this
loop
state,
so
loop
state
is
this
loop
variable,
so
we
have
the
current
index
on
it
and
the
current
length
of
the
iteration
and,
as
you
can
see,
we
wrap
it
in
an
arc.
So
an
atomic
reference
counted
wrapper
and
actually
there's
there's
already
sort
of
a
curious
part.
A
You
cannot
out
of
the
box
serialize
a
loop
controller
in
30,
even
if
loop
state
is
serializable
because
arc
is
not
serializable
out
of
the
box.
Usually
you
can
make
sturdy
serialized
arcs,
but
we
we
don't
care
about
this
right
now
it
it
will
work
for
us.
A
But
what
you
can
see
here
is
that
we're
wrapping
this
loop
state
and
we're
saying
like
one
reference
here,
actually
goes
onto
this
frame
and
then
later
we
can
actually
sort
of
hold
on
to
this
reference.
In
other
places,
too,
which
we
will
do
so
loop
state
then
refers
to
what
this
loop
variable
was
earlier,
and
I
can
show
you
how
this
happens,
but
it's
not
so
important.
A
What's
more
important
is
that
we
have
a
custom
trade
in
miniature
called
object,
and
this
object
is
used
by
anything,
that's
sort
of
dynamic
and
so
object
is
obviously
implemented
for
loop
state
and
one
of
the
things
that
it
has
is
a
call
method,
callback,
which
is
what
we
called
earlier
and
so
like.
Okay,
I
want
the
name
of
a
method,
and
then
the
arguments
passed
to
that
method
and
every
argument
is
again
this
value
object.
A
So
value
is
really
just
this
runtime
type
object
that
we're
using
everywhere,
which
is
serializable,
and
so,
if
we're
trying
to
call
the
cycle
method
and
we're
loading,
the
current
index
we're
looking
up
in
the
argument
list
index
modal
argument
length,
so
we
busy
cycling
through
and
then
we
return.
A
This
argument,
cloned,
so
value
is
clonable.
Why
is
the
value
clonable?
Well,
because
everything
in
there
is
reference
counted,
so
values
can
be
relatively
cheaply
copied.
So
this
is
roughly
what
this
looks
like
right.
So
when
the
engine
goes
say
loop,
dots
if
the
user
goes
loop.cycle,
why
is
some
sort
of
indirection
it
calls
into
this
here
and
we'll
do
whatever
is?
A
Is
done
here
if
you
try
to
call
any
other
method
on
it,
you're
going
to
end
up
in
the
second
branch
which
tells
you
there's
no
method
named
this,
so
value
is
basically
a
reference
counted
object
that
utilizes
interior
mutability
and,
more
importantly,
it
can
hold
data
that
someone
else
also
modifies
at
the
same
time
right
so
in
in
the
sort
of
where
the
user
interacts
with
template
engine
as
this
loop
object
and
a
bunch
of
other
stuff.
A
But
while
this
is
happening,
the
engine
itself
holds
also
on
to
the
loop
context
to
do
some
modification.
So
when
you,
for
instance,
iterate
so
here,
if
the
engine
wants
to
iterate
one
step
forward,
then
it
in
increments
the
index
of
the
loop
controller
by
one
it
fetches
the
next
item
from
the
iterator.
It
pushes
the
item
on
the
stack
and
if
it,
if
it
reached
the
end,
it
will
actually
just
jump
to
the
exit
condition
of
the
loop.
So
the
controller
here
is
modified
by
the
engine.
A
At
the
same
time,
someone
else
can
hold
on
to
the
controller,
so
you
can,
for
instance,
imagine
that
someone
would
reassign
the
loop
variable
to
another
variable
and
the
engine.
So
then,
you
have
like
two
pointers
to
the
same
blue
controller:
all
right
sort
of
two
handles
to
the
same
controller,
so
interior
mutability
is
happening
over
there,
so,
but
how?
What
does
this
have
to
do
with
realization
and
and
and
how
do
you
actually
serialize
the
loop
controller?
A
A
So
how
can
it
pass
through
these
internal
abstractions?
If
30
doesn't
allow
me
to
pass
data
through
that?
Doesn't
follow
its
own
object
model
right.
So,
if
you
can
imagine,
I
could
take
my
loop
context
and
I
serialize
it
into
index
and
length.
But
then
the
internal
template
just
sees
a
dictionary
of
of
index
and
length.
It
doesn't
see
a
loop
controller
anymore,
so
if
that
template,
for
instance,
through
inheritance
or
something
like
this-
wants
to
modify
or
interact
with
the
loop,
it
will
not
find
these
methods
anymore.
A
A
What
we
do
is
we
there's
a
special
internal
reserved
string,
which
starts
with
some
binary,
which
says
like
if
I,
if
you
see
this
around
later,
this
doesn't
refer
to
what
it
refers
to
normally
and
then
we
keep
some
thread
locals
the
first
fret
local
that
we
hold
is
just
a
rule.
That
says,
are
we
serializing
internally?
A
If
this
is
false,
then
it
means
someone
is
serializing,
my
value
to
json,
for
instance.
If
this
is
true,
then
we
are
serializing
for
mini
ginger
itself,
so
when
mini
changer
tries
to
serialize
for
its
own
uses,
it
will
flip
this
to
true.
A
Then
we
have
a
last
value
handle
which
is
just
the
counter
that
goes
up
into
affinity,
and
then
we
have
a
stashing
area
where
we
associate
every
handle
with
the
value
that
it
points
to,
and
then
we
just
have
a
helper
function
to
figure
out
if
we're
internally
serializing.
A
A
A
Then
we
serialize
a
struct
named
value,
handle
marker.
That
was
this
sort
of
weird
binary
one
under
under
mini
changer
on
the
value
handle.
Then
we
see
it
as
a
single
field
called
handle,
and
then
we
end
the
whole
thing,
so
we
basically
compress
our
handle
id
into
a
little
bit
of
structure.
That
is
then
internally
referenced
by
the
code.
A
A
Oh,
it's
not
in
the
deserializer
in
sort
of
destruct
serializer
we
in
we
basically
whenever
we
then
find
in
our
own
serializer
whenever
we
find
structs.
So
when
we
collect
these
structures,
we
did
earlier.
A
So
why
do
we
do
all
of
this?
So
one
example
for
this
is:
we
need
first
class
functions
in
mini
changer
to
support
what
ginger
is
actually
doing
so
this
is
this
is
valid
ginger
and
mini
changer
code,
where
you
say,
like
you,
have
a
function
called
cycler,
which
returns
an
object
which
has
which
is
callable
and
whenever
you
call
it
returns.
A
The
next
value
so
first
of
all
return
odd,
then
involve
it
and
even
every
time,
odd,
even
and
and
so
forth,
and
for
this
function
to
exist
in
the
runtime,
it
has
to
be
serializable,
and
so
this
is
what
this
looks
like.
We
have.
A
function
called
make
cycla,
which
returns
a
value
from
this
object
right,
and
so,
if
you
look
at
value
from
object,
this
creates
a
value
that
internally
holds
on
to
the
cycler,
which
has
to
be
it
has
to
implement
object.
A
A
And
the
funny
thing
with
this
is
that
from
the
engine's
perspective,
I
have
now
the
ability
to
store
any
artificial
additional
data
in
it,
and
I
can
go
through
any
layer
of
indirection
in
30.
without
having
to
build
my
own
object
system
on
top.
A
So,
for
instance,
I'm
using
this
not
only
to
hold
on
to
dynamic
objects
and
functions
in
mini
changer.
I
also
hold
on
to
special
types
of
strings
that
are
exactly
like
strings,
but
have
additional
meta
information,
so
they
can
do
something
useful
with
it.
So,
for
instance,
in
a
lot
of
template
engines,
including
ginger,
there's
a
difference
between
a
string
and
a
string
that
has
already
been
html
skate,
because
if
a
string
is
already
html
escaped,
then
I
don't
have
to
html
escape
it
again,
and
so
I
have
sort
of
two
string
types
internally.
A
Even
though
the
original
30
can
only
have
one
single
string
type,
the
sort
of
idea
of
using
inband
signaling
is
actually
something
that
even
30
itself
uses.
So
the
30
json
support
has
support
for
arbitrary
precision.
Integers,
so
integers
are
significantly
bigger
than
128
and
it
uses
exactly
the
same
system
internally
to
stash
large
strings
as
large
numbers,
and
it
also
uses
this
this
ugly
inband
signaling.
A
So
ideally,
there
would
be
a
better
way
to
do
this,
but
you
can
kind
of
do
this
anyways
by
using
tls
it's
ugly,
but
it
works,
but
it
only
works
for
as
long
as
your
serialize
and
your
serializer
cooperate.
So
if
you
were
to
serialize
a
value
not
to
sear
this
internal.
A
So
if
you,
if
you
were
to
serialize
a
value,
object,
not
to
do
many
changes-
internal
serializer
but
for
instance,
to
yaml
or
to
json-
then
none
of
this
would
work
right
because,
like
it
wouldn't
know
how
to
grab
this
stuff
from
from
from
tls.
So
we
would
actually
leak
this
out
if
we
were
to
to
to
go
through
this
code
path,
and
this
is
why
we
have
to
figure
out.
Do
we
serialize
to
mini
change,
or
do
you
serialize
to
something
else?
A
It
can
become
really
unwieldy
to
do
this,
so
you
can,
for
instance,
imagine
that
you
might
have
a
description
of
of
a
task
that
you
want
to
do,
and
this
task
involves,
like
some
input
data,
and
you
want
to
pass
this
input
data
to
another
process,
but
sometimes
you
also
have
to
pass
really
large
things
like
sockets
that
you
don't
want
to
reopen
in
the
sub
process.
So,
for
instance,
when
you're,
when
you
implement
something
like
a
web
server,
the
socket
is
already
open.
A
So
how
do
you
pass
this?
I
don't
know
if
you're
familiar
with
with
unix
ipc,
but
there's
a
call
called
sent
msg
which,
as
a
first
argument,
basically
takes
the
bytes
and
the
second
argument
takes
ancillary
data,
and
so
my
idea
was
you
can
write
a
30
abstraction
where
30
doesn't
just
produce
data.
A
It
also
collects
file
pointers,
and
so
this
is
what
I
wanted
to
do,
so
I
wanted
that
you
can
derive
serialize
and
deserialize
on
the
struct,
which
is,
for
instance,
task
and
in
this
case
the
the
first
argument.
A
The
first
field
on
this
on
the
task
is
a
tcp
stream,
but
because
tcp
stream
obviously
is
not
serializable,
I
have
to
wrap
it
in
a
new
type
which
is
called
handle,
and
this
makes
anything
that
has
a
unix
file
file,
descriptor
serializable
and
the
second
thing
is
like
a
task
payload,
and
so
this
is
what
I
want
to
do.
Is
I
want
my
sub
process.
I
want
to
connect
to
my
parent
process,
create
a
receiver,
and
I
want
to
receive
one
handle,
as
I
saw
in
this
case,
actually
task.
A
So
this
should
be
not.
It
should
not
be.
Let
handlers
should
be,
let's
task,
so
I
receive
one
task.
Then
I
want
to
compute
the
result
on
the
task
payload,
and
I
want
to
send
the
result
directly
into
the
socket
that
I
also
received
on
the
task
right.
That's
sort
of
the
goal
that
I
want
to
do
so
basic
goal
idea
is
figure
out
a
way
to
serialize
and
deserialize
the
task,
but
whenever
you
encounter
file
handle
just
put
it
somewhere
else,
so
this
is
how
serialize
is
implemented
for
handle
and
handle.
A
A
So
in
this
case,
when
it
serializes
it
checks,
am
I
in
ipc
mode.
This
is
again
the
same
logic
as
I
did
in
mini
ginger.
There's
a
thread
local
variable
that
says
true
if
we're
serializing,
five
pc
and
false.
Otherwise.
So
if
we're
in
ipc
mode,
then
we
take
the
file
descriptor,
which
is
on
ourselves.
A
A
So
originally
you,
for
instance,
had
like
say
fd45
in
there,
which
was
the
socket.
Now
it
serializes
zero
and
puts
fd45
into
an
array
in
tls
on
yeah,
so
it's
that's
the
tls
and
then
on
the
way
back.
This
is
the
deserialize
for
in
this
case.
A
Well,
I
didn't
actually
put
the
deserialize
in
here,
unfortunately,
so,
but
the
d
serialized
basically
does
the
same
it.
It
deserializes
an
integer
and
then
it
does
lookup
fd
to
find
the
fd
in
in
tls
again
and
then
get
it
out
from
there,
and
this
is
what
this
looks
like
if
you
send
it,
for
instance,
so
on
sending
it
serializes
my
original
input,
the
task,
for
instance,
into
bytes
and
instead
of
getting
just
out
bytes
like
you,
would
do
in
30.
Normally
you
get
out
the
payload
and
the
file
descriptors
independently.
A
I'm
actually
not
sure
where
my
slide
went.
That
has
this
in
there.
But
what
you
can
imagine
how
this
looks
like
is
that
I
stash
away
an
empty
vector
on
tls,
I'm
putting
my
my
file
descriptors
into
this
array.
Every
time
I'm
encountering
a
handle
and
then
at
the
end
of
it
I
just
pick
them
up
and
serialize,
and
then
I
can
send
them
independently
as
payload.
First
argument
and
fd
is
a
second
argument
and
that's
all
there
is
to
it.
A
So
I
know
this
is
a
lot,
but
you
can
find
some
of
the
code
that
cup
that
drives
this
on
my
github
and
there's
also
a
blog
post.
That
goes
with
it
on
my
blog,
which
is
linked
here,
which
explains
this
a
little
bit
more
detail.
I
think
what's
interesting
about
using
certainty
like
this.
A
Is
that,
because
30
is
so
universally
used
all
over
the
place,
there's
actually
quite
a
bit,
you
can
leverage
with
it,
even
if
it's
really
sort
of
out
of
the
ordinary
you
have
to
mention
that
there's
almost
every
single
type
in
rust
is
serializable,
and
so
there's
a
big
ecosystem.
You
can
use
if
I
were,
for
instance,
to
want
to
write
a
template
engine
where,
in
order
to
use
an
object
in
the
template
engine,
you
would
have
to
implement
my
own
trade.