►
Description
Type System Tips for the Real World by Sean Griffin
Have you ever looked at some of the more exotic capabilities of Rust's type system and wondered “why”? Why on earth would I ever want a zero sized type? How do I efficiently implement a deeply recursive type? Just what do monomorphization and type erasure actually mean?
In this talk we'll look at some real world examples from inside Diesel to answer these questions and more. You'll come away from this talk with a stronger understanding of how to use Rust's generics, traits, and exotically sized types.
A
So
just
I
checked
this
earlier,
but
the
lighting
is
a
little
different.
So
my
friend
Santiago's
in
the
back
is
this
good?
Okay,
cool?
Also
you
should
you
should
try
compiling
this
on
the
latest
nightly.
It's
pretty
funny
all
right,
so
my
name
is
Shawn
Griffin
I'm,
a
10x
hacker
ninja
guru
at
Shopify.
A
If
you're
wondering,
if
you're
wondering
what
that
means,
it
means
that
when
I
was
applying
for
my
work
permit
in
Canada
I
thought
it
would
be
funny
to
see
what
would
happen
if
I
put
that,
as
my
job
title
turns
out,
what
happens
is
I
can
no
longer
legally
work
in
Canada
as
any
job
title
other
than
10x
Akron
engine
guru.
A
These
are
the
things
I'm
known
for
making.
I
also
made
this
thing
recently.
My
friend
Steve,
who
I've
seen
give
a
lot
of
talks
at
Ruby
conferences
around
this
point.
He
always
goes
up
any.
He
said
something
along
lines
of
you
know:
I
mostly
write
rust
these
days,
but
I
still
really
love
Ruby
and
you
know
how
much
I
love
it,
because
I
haven't
won
tattooed
on
my
body,
well,
I'm,
sorry
to
one-up,
you
Steve,
but
I
literally
named
my
firstborn
child
Ruby.
So.
A
She's
also
a
big
fan
of
rust,
alright
on
to
the
actual
talk
so
that,
ultimately,
the
heart
of
software
engineering
is
managing
trade-offs
and
there
are
a
lot
of
trade-offs
in
your
choice
of
language.
For
example,
if
you
were
to
try
and
map
out
languages
on
a
spectrum
from
high
level
to
low
level,
you
might
have
something
like
Haskell
on
one
side
and
something
like
C
on
the
other,
and
if
you
were
to
just
map
out
a
arbitrary
Pro
of
Haskell.
A
A
The
combination
of
having
control
over
memory
layout,
combined
with
a
proper
type
system,
can
enable
some
really
cool
things.
Let's
take
a
look
at
an
example.
A
hash
set
is
an
unordered
collection
of
unique
elements.
It
works
very
much
like
a
hash
map
it
where
you
only
look
at
the
keys
and
not
the
values
and
rust
implements
it
exactly
like
this.
It
wraps
influence
hash
that,
as
a
very
thin
wrapper
around
hash
map.
A
A
hash
set
has
a
single
field,
which
is
a
hash
map
where
the
key
is
whatever
we
are
putting
into
the
set,
and
the
value
is
an
empty
tuple,
also
known
as
a
unit.
The
value
itself
doesn't
actually
matter
for
the
implementation
of
this
hash
set
to
be
correct.
We
could
use
any
type,
for
example,
in
Ruby
a
set
is
implemented
in
exactly
the
same
way,
but
they
use
a
boolean
rather
than
an
empty
tuple.
A
Well,
both
implementations
are
correct.
There's
a
big
performance
difference,
specifically
a
rust
is
able
to
take
advantage
of
the
fact
that
the
value
is
0
sized
and
it
eliminates
a
bunch
of
code
because
of
that,
and
that
creates
a
huge
different
perform
difference
in
performance.
How
big
well
I
did
a
very,
very
unscientific
benchmark
and
I
found
that
switching
a
hash
set
to
use
boolean
instead
of
unit
gave
me
a
20%
loss
of
performance.
A
A
You
could
start
out
with
the
simplest
case,
primitives
a
lot
of
the
primitives
and
rust
actually
happened
to
have
the
size
in
the
name
you
32
use,
16,
etc.
We
have
car
here.
Car
is
a
is
a
single
unicode
character
and
it
is
four
bytes
if
you
have
a
struct
with
a
single
field,
that
struct
is
going
to
be
the
same
as
the
size
of
its
field,
just
like
in
C.
If
you
have
more
than
one
field,
the
struct
is
going
to
be
the
size
of
each
of
its
fields
added
together.
A
So
in
this
case,
foo
is
going
to
be
the
size
of
two
cars.
Enums
act
very
much
the
same
like
strux
at
least.
If
they
only
have
a
single
variant,
you
can
pretend
that
bar
was
a
struct
and
whatever
the
size
of
bar
is
is
going
to
be
a
size
of
foo.
It
only
has
one
field
that
is
car,
so
the
size
of
foo
is
car.
A
When
you
add
on
a
second
variant,
you're
gonna
add
first
of
all
one
bite,
so
that
Russ
knows
whether
you
have
a
bar
or
a
bass,
and
then
the
size
of
foo
is
going
to
be
that
one
byte
plus
the
size
of
its
largest
variant,
and
you
can
determine
the
size
of
each
variant
by
pretending
it
worth
structs
so
bass.
Here,
if
we
wrote
as
a
struct
would
look
like
this,
it
has
no
fields
and
its
size
is
zero.
A
So
we
come
back
to
this.
We
already
looked
at
what
bar
would
be
earlier.
We
know
that
one
that
four
is
in
fact
larger
than
zero,
so
the
size
of
foo
is
gonna,
be
five.
We
changed
Baz
to
be
a
different
type,
one
that
words
larger
than
car.
Now
this
other
size
of
foo
is
gonna,
be
the
size
about
larger
type
plus
one.
A
So,
let's
look
at
a
little
bit
more
complex
type.
Let's
say
that
you
wanted
to
implement
a
new
string
type
and
you
wanted
to
implement
that
as
a
singly
linked
list,
because
there's
absolutely
no
way
that
could
ever
cause
pains
for
anybody,
so
you
might,
if
you're,
if
you're
new
to
Russ,
you
might
try
writing
it
like
this.
Let's
talk
about
finding
the
size
of
list
string,
so
we
need
to
have
first
figure
out
which
of
its
variants,
its
largest
well.
A
Nil,
has
no
fields,
and
so
it's
gonna
have
a
size
of
zero
and
I'm
gonna.
Guess
that's
not
the
largest
one
here,
so
we're
gonna
see
if
over
that,
so
we
look
at
this
variant
called
cons.
So
we
need
to
add
up
the
size
of
each
of
its
fields
head
to
the
car,
and
tail
is
a
list
room,
so
car
is
four
bytes
and
the
sides
will
list
string
well
so
to
determine
the
size
of
a
list
string.
A
First,
you
need
to
figure
out
which
of
its
variants
is
the
largest
and
oh,
no
I
actually
really
love
this
error,
because
you
basically
only
ever
run
into
it
when
you're
trying
to
implement
a
singly
linked
list,
and
so
the
and
the
error
message,
if
you
do
rusty,
explain,
is
literally
just
you're
trying
to
do
a
singly
linked
list.
Here's
exactly
the
code!
You
need
to
write,
it's
it's
great
anyway,
so
it's
complaining
because
you
a
struct,
have
this
enum
has
itself
as
a
member,
which
means
that
its
size
would
be
theoretically
infinite.
A
So
we
need
to
introduce
some
indirection
there.
So
we're
going
to
change
this
to
box
up
Lister,
so
box
is
the
heap-allocated
pointer.
There
are
those
that
aren't
familiar
with
it
and
the
size
will
point,
or
at
least
on
my
machine
is
going
to
be
eight
bytes.
So
now
the
size
of
this
whole
thing
is
the
one
bite
for
the
discriminant,
eight
bytes
for
the
pointer
four
bytes
for
the
car,
for
a
total
of
13
bytes.
A
Now
one
of
the
kind
of
curses
of
rust
is
it
makes
it
because
it
makes
it
so
easy
to
write,
really
really
performant
code
and
it
nudges
you
towards
doing
things
that
have
very
little
overhead.
It
makes
you
super
aware,
when
you're
doing
something
that
isn't
the
most
possible
performant
thing.
Is
you
so
a
lot
of
people
look
at
this
and
scream?
No,
no.
My
singly
linked
list
can't
possibly
afford
the
cost
of
a
pointer.
A
But
let's
look
at
how
we
could
how
we
could
do
this
differently
and
avoid
actually
having
to
to
do
keep
allocation.
So,
rather
than
having
list
stirring
be
an
enum,
we
could
instead
move
any
of
the
behavior
that
we
needed
to
group
together
here
onto
a
trait
and
have
two
structs.
Instead,
so
I've
omitted
the
trait
here,
but
we
have.
We've
turned
each
of
our
variants
into
a
completely
separate
struct
and
specifically
the
case
of
cons
where
we
have
at
least
one
element
is
now
generic
over
the
rest
of
the
list.
A
I
haven't
talked
yet
about
about
how
to
calculate
a
size
of
a
generic
struct.
So
let's
say
we
had
a
star
called
pizza
pizza
generic
over
its
topping.
So
if
we
were
to
say
have
a
topping,
let's
call
pineapple
and
we
asked
the
compiler
what
the
size
of
a
pineapple
pizza
was.
It
would
error
because
pineapple
doesn't
belong
on
a
pizza,
Steve.
A
No
so,
of
course,
pineapple
has
a
size
of
zero.
If
we
pretended
that
topping
but
we're
pineapple,
then
this
would
be
a
single
field
that
is
of
the
type
pineapple
that
we
have.
That
has
a
size
of
zero,
so
the
struct
has
a
size
of
zero
and
if
you
had
a
pizza
with
a
giant
hole
in
it,
get
it
cuz
cuz
it's
topping
as
a
byte.
A
Then
its
size
would
people
invite
it,
so
the
size
of
our
construct
here
is
going
to
be
the
size
of
a
car
plus
the
size
of
the
rest
of
the
list.
So
it's
interesting
here
is
that
the
size
of
the
entire
list
is
going
to
be
exactly
the
smallest
size
possible.
The
size
of
an
empty
list
is
going
to
be
zero.
A
The
size
of
a
list
containing
one
character
is
going
to
be
the
size
of
exactly
one
character
and
if
it
had
two
characters,
it'll
be
positive,
two
characters,
three
characters
so
on
there's
some
other
interesting
properties,
for
example,
you're
now
actually
encoding
the
length
of
your
string
in
the
type
system,
so
things
like
length
will
I
can
potentially
get
in
line
by
the
compiler
and
replaced
with
just
a
literal.
You
could
also
you
could
also
do
generic
code
and
implement
traits,
for
example,
only
for
empty
strings
and
have
that
verified
at
compile
time.
A
You
can
no
longer
write
code
like
this.
If
you
want
to
take
a
list
string
as
an
argument,
you
can't
just
take
a
type
called
list
ring
you
have
to
take.
You
have
to
write
a
generic
function,
you're
taking
some
type,
T
or
T
implements
list
string,
and
if
nothing
else,
this
just
looks
more
complicated
to
me,
I,
really,
if
you
what
I
think
about
it's
like
no,
but
it's
really
not
doing
that
much
different,
but
you
just
look
at
it.
A
You
also
can
no
longer
pattern
match.
So
if
you
want
to
write
a
function
in
that
determined,
if
your,
if
your
list
string
came
the
letter
A,
you
might
write
it
like
this,
and
if
you
were,
if
you
were
to
do
a
generic
Li,
you
can
no
longer
assume
that
you
either
have
specifically
cons
or
nil.
Since
list
string
is
now
a
trait
theoretically,
any
struct
could
actually
implement
it.
A
So
you
can
kind
of,
like
maybe
write,
a
function
that
returns
the
head
and
an
option,
the
head
and
tail
and
do
something
kinda
similar,
but
just
in
general,
your
codes
just
gonna,
feel
a
little
bit
more
complex,
you're
gonna
be
you're
the
the
cost
that
you're
gonna
be
paying
for.
This
is
going
to
be
a
complexity
in
your
codebase
now
for
a
little
interim
from
Ruby.
If
you
need
hype,
if
you
need
high
quality,
Russ
code
or
training,
Ruby's
thinks
you
should
go
with
integer
32
LLC.
A
So,
let's
look
at
some
more
code
here.
Let's
look
at
what
happens
when
we
have
when
we
when
we
deal
with
traits,
so
we
have
a
trait
called
robot
robots
have
user
name.
We
have
a
lot
of
great
robots
in
rust.
They
they
manage
the
rust
repo
for
us,
they're,
great,
we're,
gonna,
say
hi
to
them,
because
we
love
our
robots.
We
need
their
username
just
ahead
of
them.
One
of
our
robots
is
named.
Boris
force
is
responsible
for
merging
all
of
the
code
into
the
rest,
repo
and
running
CI
and
doing
other
great
things.
A
Alex
is
responsible
for
implementing
things.
It's
a
sentient
AI.
So
when,
when
the
compiler
is
going
through
this
code,
it's
gonna
go
in
traits,
aren't
really
a
thing
that
exists
in
your
final
binary.
You're
offering
system
doesn't
know
what
a
trait
is
or
how
to
call
a
method
on
a
tree,
so
the
mod
is
gonna
go
and
it's
gonna
replace
these
functions
with
just
an
actual
plain
function,
so
it
the
names
are
just
sort
of
made
up
by.
In
this
case,
we
could
pretend
it
was
called
robot
user
named
Boris.
It's
gonna
go.
A
Take
this
one
and
turn
that
into
robot
username
Alex,
now
our
say:
hi
function.
What
Russell's
gonna
do
when
we've
written
it
this
way
is
it's
going
to
do
a
transformation
to
a
called,
a
mono,
more
physician,
which
is
a
big
scary
and
kind
of
poorly
named
term?
But
this
function
right
now
is
polymorphic.
It
can
take
multiple
types
and
what
mono
more
efficient
means
is
that
the
compiler
is
gonna.
Take
this
function.
That's
gonna
copy
it
for
every
type
that
you're
calling
it
with.
A
A
This
is
the
the
benefit
of
this
optimization
in
a
vacuum
is
just
that
you
get.
You
have
one
less
dynamic
function.
Call
it's
not
a
huge
win
on
its
own,
but
this
enables
other
optimizations
to
occur,
because
now
the
compiler
can
go.
Look
at
this
function
and
say:
okay,
well,
robot
username,
Bork's,
that's
a
really
simple
function:
I
can
just
inline
that
and
then
it
can
do
the
same
thing
over
here.
A
This
is
a
bad
as
far
as
the
compilers
gonna
be
able
to
go.
It
can't
quite
take
that
last
step
to
look
at
this
and
say
well.
I
could
just
combine
both
of
these
into
a
single
string.
Literal
be
cool
if
it
did,
but
you
know
what
that's
fine,
as
with
all
things
in
programming,
there's
a
trade-off
here.
The
cost
that
we're
paying
for
the
compiler
during
more
efficient
code
is
that
it's
increasing
the
size
of
our
binary.
A
It's
copying
these
functions
over
and
over
again,
and
it's
going
to
likely
increase
your
compilation
times
as
well.
In
some
cases
you
can
opt
out
of
this
if
you'd,
rather
not
pay
that
cost
by
taking
a
reference
to
robot
we're
taking
what's
called
a
trait
object,
sometimes
also
referred
to
as
type
eration.
A
When
we
take
a
trait
object,
the
compiler
and
no
longer
knows
anything
about
the
specific
type
that
we've
passed
to
it.
The
type
has
been
erased.
So
what
that
means
is
that
a
compiler
can't
do
a
lot
amorphous
ation.
It
can't
generate
a
unique
version
of
this
for
the
tiger
passing
code
because
it
doesn't
know
what
type
that
is,
but
it
also
means
that
the
function
is
not
going
to
get
optimized
beyond
this
point.
This
is
as
far
as
the
compiler
is
gonna
go.
It
can
no
longer
see
past
user
name.
A
Behavior
Ruby
says
if
you
also
have
trouble
figuring
out,
which
way
to
hold
a
bottle:
integer
32
LLC,
just
all
of
my
cute
baby
pictures
Hank,
noticed
we're
in
rust
one
season,
it's
great
all
right,
so
this
is
Texas
and
tricks
for
the
real
world,
and
I've
only
talked
about
some
world
where
people
use
singly,
linked
lists
for
string,
and
that's
just
crazy
talk.
So
let's
talk
about
it.
Real
world
example.
A
A
A
There
it
is
no
data,
there
is
nothing
you
can
do
with
them
at
runtime.
So
if
a.type
is
zero
size,
it
means
that
you've
structured
your
code
in
such
a
way
that
everything
you're
doing
with
it
isn't
is
something
that
is
completely
done
at
compile
time.
The
type
itself
is
guaranteed
to
be
erased,
because
there
is
nothing
you
can
do
with
it
so
make
this
a
little
bit
more
clear,
I'd
like
to
go
through
what
the
compiler
is
going
to
do
in
when
diesel
tries
to
construct
the
sequel
for
this.
A
For
this
query,
use
this
not
find
one.
This
would
generate
select
star
from
users
where
users
dot
ID
equals
one.
The
type
that
is
that
this
expression
is
going
to
return,
looks
like
this
in
the
interest
of
keeping
things
a
size
that
fits
on
a
slide.
We're
gonna
omit
a
lot
of
this
code
and
pretend
they're
type
only
looks
like
this.
Instead,
which
is
still
a
slightly
large
type,
but
is
smaller.
Most
of
the
things
that
I
just
took
off
are
types
that
are
zero
size
and
basically
are
saying.
A
If
this
part
of
the
query
isn't
there
and
are
guaranteed
to
get
eliminated,
if
you
want
to
construct
it,
you
would
do
it
like
this.
This
isn't
super
the
specifics
of
this
isn't
super
relevant,
but
the
point
being
that
these
are
all
actual
concrete
types.
There
is
nothing
behind
a
box.
There
is
no.
There
are
no
trade
objects
involved.
A
A
The
definition
of
push
sequel
is
going
to
get
in
line,
but
we're
gonna
leave
it
alone
for
now
and
come
back
to
it
later
this
section
here,
we
know
that
the
type
of
self
not
distinct,
is
no
distinct
clause,
so
we
could
just
replace
it
like
that.
This
function
is
getting
in
line.
The
body
of
that
function
is
literally,
do
nothing,
and
so
that
line
gets
deleted.
A
We
know
that
self
dot
select
is
of
the
type
of
default,
select
clause
and
the
body
of
default.
Select
Clause
is
grab
the
default
selection.
The
default
selection
is
a
tuple
of
all
of
the
columns,
and
this
is
going
to
inline
to
the
definition
of
walk
ast
for
a
two
element:
tuple,
which
is
going
to
look
a
little
bit
funky,
because
this
code
is
generated
by
a
macro,
but
it's
okay,
because
the
compiler
is
going
to
come
and
clean
up
my
mess
for
me
now.
You
know
in
a
massive
breakthrough
of
computer
science.
A
It's
also
able
to
figure
out
that
code
that
behind,
if
false,
will
never
get
run
so
that
just
goes
away
same
thing.
Massive
breakthrough,
one
is
in
fact
not
equal
to
zero
and,
if
true
is
always
going
to
get
run,
so
we
just
replace
that
with
push
a
comma
going
up
to
here,
users
ID
first,
we
first
we
put
use
push
users,
then
we
push
a
dot
and
then
we
push
ID
push.
A
Identifier
is
going
to
put
a
double
quote,
and
that's
going
to
put
the
the
identifier
that
we
want
to
put
out
there
with
replacing
all
double
quotes
with
double
double
quotes.
Power
is
also
going
to
be
able
to
see
that
there
are
no
double
quotes
in
this
string,
so
we
just
eliminate
that
entirely
same
thing
happens
for
ID
users,
name
oh
hold
on,
let's
scroll,
let
me
scroll
up
here.
Yeah
run,
yes,
really
have
space
same
thing
from
clause,
that's
just
the
users
table.
We
know
how
to
do
that
where
Clause.
A
So
we
know
specifically
that
we
do
have
a
where
clause,
so
this
gets
in
line
and
we're
just
gonna
push
we're
out
there
and
then
we're
gonna
and
then
we're
gonna
push
the
actual
value
of
the
where
Clause
the
where
Clause
is
ID
equals
one.
So
first
we're
gonna
put
on
users
ID
and
then
we're
gonna
put
out
one
now.
One
is
interesting,
because
this
is
the
only
part
of
this
query
that
is
in
any
way
dynamic.
So
this
is
just
gonna
be
the
code
that
has
pushed
this
dynamic
value.
A
A
So
this
is
the
code
that
we
started
with,
and
this
is
what
it
got
optimized
down
to
now.
You'll
notice,
two
things
about
this
number
one.
This
is
very
much
larger
than
what
was
there
before.
We
also
does,
if
you
squint
at
it
it's
really
flat.
There
are
no
conditionals
here,
there's
no
dynamic
dispatch
going
on
this
is
sort
code
that
your
CPU
is
going
to
go
through
very
very
quickly.
A
So
let
me
go
to
this
line.
Push
sequel,
so
this
is.
This
is
the
what
happens
when
that
function
gets
in
mind?
Yes,
she
passed.
The
thing
that
we've
been
operating
on
here
is
an
enum
diesel
double
actually
call
this
function
four
times
once
with
each
variant
of
the
enum
to
do
all
the
different
things
that
we
want
to
do
to
the
with
that
value,
the
one
that
we're
caring
about
right
now
is
actually
constructing
the
sequel
string.
A
So
that's
called
the
two
sequel
pass,
but
because
this
function
will
get
inlined
itself
and
the
line
immediately
before
we
call
the
function
will
be
the
thing
that
constructs
the
ast
pass
like
POW
is
actually
just
going
to
know.
Oh
this
is
this:
is
we
always
have
that
variant?
So
we
can.
We
don't
need
to
have
that
conditional.
A
We
know
that
we're
always
going
to
run
this
code,
and
then
the
body
of
flesh
sequel
is
is
just
pushes
through
poster,
which
is
a
method
on
string
from
the
standard
library,
and
so
it's
gonna
do
the
same
thing
to
all
of
these
other
lines.
I'm
not
gonna,
make
you
watch
every
single
one
of
those
get
transformed
and
again.
This
is
this
is
as
far
as
the
compiler
is
gonna
be
able
to
take
it.
A
It
is
not
able
to
then
take
that
last
step
that
would
look
really
cool
on
a
slide
if
it
did
and
turn
that
into
a
string
literal,
but
it's
about
as
close
as
it
can
get.
This
is
effectively
letting
mute
s,
equals
4,
equals
string
new
and
then
pushing
each
of
these
little
fragments
onto
it.
The
the
bind
parameter
where
the
one
goes
at
the
very
bottom.
A
That's
just
gonna
get
in
line
to
push
the
sequel
for
a
placeholder
for
a
value
that
gets
sent
separately,
and
this
is
mildly,
interesting
and
a
decent
win
for
constructing
the
sequel.
Query.
What's
really
interesting
is
is
what
happens
for
our
other
three
ast
passes
rails
when
it
constructs
a
sequel.
Query
has
to
go
through,
goes
through
a
very
similar
structure
and
wants
all
the
same
information
that
diesel
has
when
I'm
doing
this
in
rails.
I
have
to
try
very
hard
to
shoehorn.
All
of
this
into
a
single
call.
A
We
cannot
do
multiple
passes
over
the
ast,
because
it's
very
expensive,
but
in
rust
this
is
the
pass
where
we
actually
collect
the
dynamic
data.
So
we
still
care
about
this
push
by
that.
Last
call
of
push
bind
brand,
which
is
that
cut
in
line
to
push
bound.
This
is
the
thing
where
we'll
actually
serialize
the
one
to
the
bytes
that
the
database
cares
about.
A
If
we
had
more
than
one
value,
there
might
be
some
additional
optimizations
of
specifically
the
order
that
we're
pushing
things,
but
it's
not
terribly
interesting.
What's
important,
though,
is
all
of
that
code
that
would
have
been
conditionally
if
we're
in
this
past
do
this
or
all
the
code
that
was
there
for
traversing
the
ast
for
everything
that
didn't
have
a
dynamic
piece
got
eliminated.
A
When
people
talk
about
building
zero
cost
abstractions,
these
optimizations
are
what
make
that
possible.
This
is
what
makes
iterator
fast.
So
what
makes
futures
fast?
This
is
what
makes
hash
that
fast.
At
the
beginning
of
this
talk,
I
mentioned
that
using
unit
instead
of
bool
was
about
10
to
20
percent
faster,
but
I
glossed
over.
Why?
That
was
the
case.
A
A
Now
the
compiler
could
have,
in
theory,
done
that
same
optimization
by
looking,
oh
well,
we're
just
never
using
the
values,
let's
eliminate
the
values
in
this.
In
practice
it
just
happens
not
to,
but
the
point
of
that
is
the
fact
that
this
type
of
0
size
it
doesn't
enable
the
optimization,
but
it
does
guarantee
it
and
what
we
end
up
with
at
the
end
of
the
day
when
we
get
to
our
machine
code,
is
the
same
code
that
we
would
have
if
we
wrote
a
super
optimized
hash
set
ourselves.