►
From YouTube: RustLatam 2019 - Michael Gatozzi: Defense Against The Wrong Logic: Proactive Rust Coding
Description
Rust guarantees memory safety, but it doesn’t stop you from doing everything. From unsafe to unwrap and more, there are lots of ways for you to subvert and make your code fail due to our own mistakes: logic bugs and incorrect assumptions.
Come along and learn Rust techniques and patterns that will help build safe guards into your code, test your assumptions, and get a better understanding of what Rust and you can guarantee with your code.
All so you can sleep sounder at night and not be woken up at 3am while on call.
Follow us on Twitter: https://twitter.com/rustlatamconf
A
All
right,
so
this
talk
is
entitled
defense
against
the
wrong
logic,
proactive,
rust
coding,
pretty
much.
This
talk
is
going
to
be
about
how
we
can
use
a
couple
guidelines
on
some
things,
as
well
as
using
the
compiler
in
order
to
make
sure
that
you
can
make
sure
that
you
actually
can
stop
yourself
from
doing
logic
bugs
because
rust
prevents
you
from
doing
a
lot
of
you
know
it
prevents
you
from
doing
data
races
and
things
like
that,
but
it
doesn't
exactly
stop
you
from
like
doing
the
wrong
thing
yourself.
A
A
So,
let's,
let's
go
over
what
we're
gonna
cover
today.
So
there's
quite
a
few
things:
we're
gonna
cover
what's
new
types
as
well
as
what
the
Builder
pattern
is
dealing
with.
Converting
input
insert
assertions.
What
what
to
do
when
you
unwrap
and
expect,
as
well
as
a
little
bit
of
unsafe
guidelines
when
you're
writing
your
code?
So
let's,
let's
get
started
out
new
types.
A
So
let's
say
we
have
some
code
here.
You
know
we
want
to.
We
want
to
create
a
temperature
conversion,
library
between
Fahrenheit
and
Celsius.
So
you
might
say:
okay,
I'll
have
a
type
Fahrenheit
I'll
make
it
a
32-bit,
integer
and
I'll
have
a
type
Celsius
and
I'll
make
it
a
32-bit,
integer
and
I.
Have
this
function?
A
A
Yes,
unfortunately,
you
can't
you
can
do
that,
and
the
reason
is
that
we
just
used
what
was
known
as
a
type
alias.
We've
said
that
they're
both
32-bit
integers
and
that
you
can
put
that
in
there
and
that's
not
really
good.
We
don't
want,
or
we
don't
want
to
put
Fahrenheit
inside
of
Celsius
or
Celsius
since
had
a
Fahrenheit,
and
when
you
deal
with
these
kind
of
conversions
between
units,
you
have
a
lot
of
problems.
A
In
fact,
you
know
some
of
the
space
like
spaceships
have
like
blown
up
in
flight
because
they
were
dealing
with
meters
instead
of
feet
or
feet
instead
of
meters,
and
that's
not
good.
So
we
can
prevent
that.
We
can
do
this
in
a
way
using
what's
known
as
a
new
type,
you
define
destruct,
and
then
you
give
it
some
internal
value
with
that
without
a
name
for
the
field.
A
So
in
this
case
we
said,
okay,
we
have
Celsius,
it
has
an
internal
field
called
a
32-bit
field
and
we
have
a
struct
called
Fahrenheit
and
Fahrenheit
has
as
well
a
32-bit
integer
inside
of
it.
So
let's,
let's,
let's
try
that
code
again,
let's,
let's
look
at
it
a
different
way,
so
we've
taken
this
new
type,
we've
called
it
Celsius
right
it
has
32,
is
the
number
inside
of
it
and
we've
assigned
it
to
the
value
temp
and
then
we
try
to
put
it
into
this
function.
A
Is
it
hot
and
if
it
is
it'll
say
yes,
otherwise,
they'll
say
no
right.
Will
this
compile?
The
answer
is
no,
it
will
not
compile,
and
the
reason
being
is
that
we've
defined
a
struct,
a
type
in
our
system
called
Celsius,
and
we
our
function
is
taking
a
struct
called
Fahrenheit
and
Russ
is
gonna.
Look
at
they
can
go.
These
aren't
the
same
thing.
What
are
you
doing?
Don't
put
this
in
here
and
so
we
can.
Then
you
can
utilize
this
type
system
as
a
way
to
make
sure
that
you
know.
A
But
you
know
it's
kind
of
useful
to
be
able
to
convert
things.
We
want
to
be
able
to
say
well,
Celsius
is
a
unit
of
temperature,
and
so
is
Fahrenheit.
Wouldn't
it
be
nice
if
we
could
like
convert
that,
and
we
can
there's
the
into
trait
and
we
could
define
this,
we
can
say:
okay,
we
can
implement
into
four
cells
into
four
Celsius
for
Fahrenheit,
so
it
takes
a
Celsius
input
and
here's
how
we
would
convert
it
right,
and
so
we
can
do
this
in
such
a
way
that
we
can
now
convert
between
things
safely.
A
The
way
to
look
at
it
is
that
types
and
structs
and
things
they
represent
ideas
or
objects
right
in
this
case
we're
trying
to
represent
a
unit
of
heat
but
traits
defying
behaviors
behaviors.
Allow
us
to
tell
how
these
types
work
and,
as
a
result,
we
can
do
things
that
we
couldn't
do
if
we
just
use
the
type
alias
and
dealing
with
primitives
like
a
32-bit.
A
Integer
is
just
a
number,
but
a
new
type
is
a
number
with
meaning,
and
so
we
can
therefore
define
ways
to
give
that
meaning
and
those
those
values
that
we're
dealing
with
behaviors.
So
in
this
case,
here's
how
we
define
Celsius,
you
might
notice
that
says:
dot
zero.
That's
how
you'd
pull
out
the
inner
value
of
a
new
type
as
well,
so
we're
saying:
okay,
we
want
the
value
inside
of
Celsius,
we'll
add
nine
to
it,
divided
by
five
and
add
32.
A
Now
there
is
one
kind
of
iffy
thing
about
this,
and
that
is
that
we're
kind
of
using
as
I
32,
if
you
were,
you,
know,
actually
making
a
really
good
temperature
library.
You
probably
wouldn't
to
do
that
this.
This
would
truncate
any
results
that
are
not
that
our
decimals,
but
this
is
just
an
example
as
to
kind
of
give
you
an
idea
of
like
what
you
can
do
with
this.
So
let's
look
at
that
again,
you
know
we'll
define
the
temperature
and
then
we
can
call
dotty
in
two
and
then
this
will
compile
this.
A
A
A
So
what's
the
Builder?
What's
the
Builder
pattern
used
for,
we
want
to
use
it
to
avoid
invalid
states.
What
I
mean
by
this
is
someone's
using
your
library
and
you've
defined
a
pathway
to
make
sure
that
they
cannot
do
things
that
will
not
work.
They
can't
do
something
that
would
cause
the
program
to
you
know,
take
input
that
would
you
know
it
would
compile,
it
would
run,
but
it
wouldn't
exactly
work
the
way
they
expect.
A
You
basically
have
a
logic
bug
or
a
runtime
error,
and
so
what
we
do
is
that
we
use
types
to
represent
these
states.
What
we're
saying
is
that,
like,
okay,
like
you
know,
here's
here's
one
type
that
represents
this
here's
one
type
that
represents
that
and
then
we
find
a
way
to
change
it,
and
so
by
this
we
use
functions
to
change
these
states,
and
so
what
you're
doing
is
you're
doing
a
defined
pathway
for
people
to
move
from
one
place
to
the
next.
A
If
this
sounds
like
a
graph,
it
is,
and
so
here's
an
example,
so
let's
say
we're
building
a
library
to
connect
to
github.
We
want
to
be
able
to
do.
We
want
to
interact
with
the
API
we
want
to.
You
know,
do
a
variety
of
things
and
I've
done
this
in
my
own
library,
and
so
we'll
have
a
client
type
that
lets
us
do
all
kinds
of
requests.
A
You
know
we
wanted
to
get
request,
there's
delete,
requests,
post,
requests,
requests,
put
requests
all
kinds
of
separate
things
that
you
can
do
now.
What
we
do
to
get
to
those
states
is
I
will
say:
okay,
we'll
call
get
or
delete
or
post
or
put,
and
that
brings
us
to
the
next
type
right,
but
this
type,
when
you
implement
you
know
you'll
say
like
info
delete
or
imple
get
right.
These
only
have
a
specific
amount
of
functions
that
they
can
use
right.
A
You
can't
just
all
of
a
sudden
post
and
then
to
like
I
use
a
repo
like
that's
not
allowed,
but
we
do
have
get
and
delete
which
allow
to
get
a
users
repo,
and
so
these
functions
allow
us
to
transition
from
one
to
the
next
and
it's
a
little
bit
easier.
If
you
see
it
kind
of
in
action,
so
this
is
kind
of
what
it
would
end
up.
A
Looking
like
right,
we
have
a
client,
we
want
to
get
some
user,
we
will
want
to
get
their
repo
and
then
we'll
like,
send
the
request,
and
so
the
Builder
pattern
allows
you
to
build
up
a
set
of
functions
in
order
to
kind
of
do
these
changes.
Oftentimes
you'll
see
it
with
like
iterator
adapters.
You
might
call
like
inter
map
and
then
collect
right.
A
A
Diesel
does
a
lot
of
like
really
interesting
some
other
type
level
hacks
to
like
really
make
sure
that
you
can
only
do
very
specific
things,
and
it
ends
up
allowing
you
to
like
make
sure
that
you
don't
do
like
database
queries
that
do
not
work
right
things
that
you
might
not
catch
until
you
know
it's
5:00
a.m.
in
the
morning.
Production
is
down
and
you're
getting
a
phone
call.
A
So,
let's
take
an
example:
let's
say
that
we
are
making
a
request
to
some
website.
You
know
we'll
say:
okay
I
want
to
make
this
HTTP
request.
You
know
and
I
get
the
value
back,
and
it's
just
the
HTTP
request.
An
HTTP
has
been
around
for
quite
some
time.
It's
a
lot
of
just
strings
and
you
could
say:
okay
well,
I
want
to
get
the
status
code
of
this
and
you
could
go.
You
could
iterate
through
the
lines
find
the
status
code
parse
it
grab
the
number
and
then
check.
A
Was
it
like
a
200
or
was
it
a
404
or
whatever
the
case
might
be,
but
that's
really
prone
to
errors,
you're
kind
of,
if
you
just
pass
the
string
around,
there's
no
guarantees
that
it
hasn't
changed.
There's
no
guarantees
that
you
didn't
parse
it
correctly
or
there's
no
guarantees
that
you
parsed
it
correctly
you're
dealing
with
a
lot
of
uncertainty.
The
boundaries
of
input
is
just
a
bunch
of
strings
that
you're
trying
to
deal
with
as
a
type
is
not
exactly
the
best.
A
It
would
be
better
if
you
had
something
more
like
this,
where
you
made
a
request
and
you
got
a
response.
You
have
a
field
headers
where
you
have
all
the
headers
parsed
out
correctly,
you
have
some
status
code,
you
have
a
body
made
of
JSON.
These
are
types
that
you
can
again
deal
with.
They
are
types
that
again
have
behaviors
and
traits
that
you
can
work
with.
A
There
are
things
that
you
can
match
against.
The
status
code
could
be
an
enum
that
you
can
match
against
every
possible
thing
that
happened.
You
could
say:
oh
I
got
a
404
I
want
to
throw
an
error
or
oh
I
got
a
200
I
want
to
make
sure
that,
like
we
continue
on
with
whatever
we're
doing
types,
allow
us
to
check
a
lot
of
things
and
the
faster
that
you
get
things
into
types.
A
The
better
traits
traits
and
types
are
very
powerful
things
that
allow
you
to
do
a
lot,
a
lot
of
interesting
things
and
you're
able
to
check
your
work
with
the
compiler.
The
compiler
ends
up
being
your
friend.
Rather
than
being
this
antagonist,
it's
able
to
make
sure
it's
got
your
back
in
a
way
so
with
that.
I
also
want
to
mention
that
Saturday
is
a
fantastic
crate.
If
you
want
to
deal
with
transforming
string
based
input
into
axle
types,
it
makes
it
super
super
easy
I
used,
Saturday
JSON
a
lot.
A
For
instance,
you
know
you
define
a
structure
like
okay,
here's
what
it
looks
like
it
kind
of
looks
like
the
JSON
in
a
way,
and
then
you
say:
okay
derive
serialize
or
derive
deserialize,
and
then
it
just
works.
You
call
the
function
saying:
okay,
here's
my
string,
here's
the
input
turn
it
into
this
struct
and
then
that's
it,
and
so
there's
a
lot
of
these
kinds
of
crates
that
deal
with
the
fact
that
we're
gonna
have
a
lot
of
string
as
input
at
the
boundaries
of
your
program.
A
There's
also
parsing
libraries
like
nom
or
pests
that
you
can
use
those
as
well
in
order
to
like
write
your
own
parser
if
you're
trying
to
like
write
your
own
language
or
something
like
that,
you
can
use
these
things
in
order
to
parse
input.
But
I
think
the
key
point
I
want
to
make
sure
is
that
you
use
types
types
are
powerful
and
the
less
you're
dealing
with
a
string
based
API
the
easier
it
is
to
catch
errors
that
you
might
have.
A
It
tends
to
be
the
case
in
like
larger
programs
like
if
we're
not
able
to
load
up,
you
know,
for
instance,
a
configuration
file
or
the
cache
that
you're
using
gets
invalidated
in
a
really
bad
way
that
it
subtly
breaks
everything.
Assertions
are
ways
to
make
sure
that
you
can
kind
of
just
say.
You
know
what
let's
just
stop
and
not
continue
running
the
program.
A
So
there's
a
couple:
there's
also
assert
not
equal
and
debug
assert,
not
equal
I
just
forgot
to
put
down
the
slides,
but
assert
says
that
you
know
okay,
I
will
take
some
condition
or
something
that
evaluates
to
be
lien,
yes
or
no
start
equals
just
says.
The
thing
on
the
left
side
is
equal
to
the
thing
on
the
right
side
and
debug
assert
does
the
same
but
they're
slightly
different,
how
they
work.
A
A
I've
had
a
Twitter
bot
that
ran
for
like
six
months,
that
I
wrote
that
was
like
a
hundred
two
hundred
lines
of
code
and
the
only
reason
it
stopped
working
was
because
the
server
rebooted,
not
because
the
code
was
bad
or
anything,
and
also
like
when
you're
prototyping
and
you're,
just
working
on
things
and
you're,
just
like
trying
to
get
something
working
really
quickly.
It's
great,
you
know
you
just
you're,
saying
I,
don't
care
if
the
result
is
an
error
and
I
don't
care.
A
A
I
spent
about
two
weeks
or
a
week
of
time,
just
trying
to
remove
all
that,
and
it
was
the
hardest
refactoring
I've
ever
had
to
do,
and
then
the
PR
never
ended
up
getting
merged
because
it
was
just
too
big
in
order
to
kind
of
go
okay,
we
understand
all
these
changes
that
are
being
made
once
it's
in
your
codebase.
It's
kind
of
hard
to
take
out
so
I
have
a
few
guidelines
regarding
this.
A
A
If
all
of
your
programs
using
it
from
the
beginning,
it's
not
hard
to
add
it
in
versus,
if
it's
not
using
it
from
the
beginning,
it's
a
lot
harder
to
add
it
in
if
you
can
and
if
you
do
need
to
use,
expect
or
unwrap
or
unwrap.
Try
to
use,
expect
nothing's
worse
than
having
the
code
crash
and
you're
not
really
sure
where,
because
the
stacktrace
just
says
that
it
failed
inside
of
Lib
core
and
that's
not
useful,
you
don't
know
where
the
program
crashed
and
so
with
expect.
A
You
can
give
it
a
like
a
little
bit
of
a
string
like.
Oh,
we
express
that
we
expected
two
plus
two
to
equal
4,
but
it
was
5
for
some
reason,
and
then
you
can
kind
of
search
for
that
in
your
code
base,
you
can
use
like
brick,
prep
or
just
grep,
and
you
can
find
that
you'll
be
able
to
kind
of
go
OK
like
I
know
where
it
crashed.
At
least
we
can
go
from
here.
A
And
so,
if
you
do
use
expect
and
unwrap,
you
should
only
use
it
if
you
know
it'll,
never
panic
what
you're
telling
the
compiler
is
you're,
saying
I
know
exactly
why
this
is
fine.
I'm
saying
don't
worry
about
it.
This
will
always
be
a
value,
or
this
will
never
be
an
error.
The
compiler
doesn't
know
that
the
compiler
can
check
types.
They
can
do
borrow
checking,
but
it
doesn't
know
everything
it's
only
so
smart
and
we,
as
the
programmers,
can
know
certain
things
about
it
like
we
could.
Look
at
that
program.
Go
oh
yeah!
A
So
if
you
do
do
that,
you're
telling
the
compiler
this
that
it
is
fine
in
some
cases,
it's
totally
perfectly
fine,
oftentimes
I
will
use
it
with
like
the
standard
library
mutex
to
just
unwrap
it.
When
I
get
a
lock
because
I'm
never
going
to
crash
the
program
such
way,
that's
gonna
like
poison,
the
lock,
for
instance.
A
So
if
you
do
do
that,
though,
try
and
leave
a
comment
or
as
as
to
why
it's
okay,
it
might
be
obvious
for
you,
but
for
someone
else
coming
to
read
your
codebase,
maybe
or
even
yourself,
six
months
later,
if
you're
in
that
spot,
you
might
not
know
why
or
you
might
not
have
the
context
or
you
might
not
be
someone
who
does
know
these
kinds
of
things
and
you
want
to
be
able
to
kind
of
tell
people.
This
is
okay.
This
is
not
a
bad
thing
like
here's.
Why
so.
A
A
But
the
nice
thing
is
that
a
lot
of
the
compiler,
a
Ness
standard
library
deal
with
this.
For
you,
a
lot
of
smart
people
have
spent
some
time.
Writing
it
to
make
sure
that
we
have
some
really
safe
code.
That's
really
performant
that
allows
us
to
deal
with
all
this
stuff.
Veck
is
a
really
great
example.
You
don't
have
to
worry
about
a
global
array.
A
You
just
throw
values
into
it
and
it
works
I
like
to
call
it
the
memory
safe,
the
memory
safe
allocator
for
rust
developers,
in
the
sense
that
you
don't
have
to
worry
about
memory
allocation
Beck
just
does
it
for
you
so
with
unsafe.
What
you're,
trying
to
tell
the
compiler
is
that
this
is
fine.
This
is
OK
code.
You
can't
I
can't
prove
that
to
you,
but
you
should
listen
to
me
and
just
trust
me
I
think
that's
kind
of
what
we
should
have
called
unsafe
who's.
Just
trust
me
because
it's
it's
not
really
unsafe.
A
So
much
as
it
is
just
you
have
to
be
careful,
you're
you're,
upholding
invariance
laws
like
a
contract.
You
have
this
contract
with
the
compiler,
and
the
compiler
is
expecting
you
to
uphold
that
contract.
You
don't
want
to
break
the
compilers
trust,
because
what
happens
up
happening
is
that
you
end
up
having
like
undefined
behavior
seg
faults,
all
kinds
of
not
fun
things.
The
things
that
rust
is
trying
to
prevent
but
safe
rust
can
only
do
so
much
safe.
Rust
knows
it's
rules,
and
it's
says:
okay.
A
So
some
guidelines
with
that
you
want
to
minimize
the
scope
of
unsafe
calls.
You
want
to
kind
of
keep
it
to
one
line
if
you
can,
or
maybe
like
a
block,
the
less
less
kind
of
on
safe
that
you
use
the
better
it's
gonna
be
for
you
and
the
reason
being
is
that
it's
a
lot
easier
to
think
about
it's
a
lot
easier
to
navigate
about
in
your
code.
If
you
have
just
a
whole
lot
of
the
code
is
unsafe.
A
It's
tagged
unsafe,
but
really
only
a
portion
of
it
should
have
been
it's
kind
of
hard
to
think
about.
Oh
did
and
especially
if
someone
else
comes
in
much
later
and
helps
work
on
your
code
base.
Oh
is
that
unsafe.
Why
is
that?
And
they
have
to
think
about
all
these
things.
If
you
just
say
no,
this
pointer
read
this
raw
pointer,
read
is
unsafe
and
you
just
leave
it
there.
Then
it's
a
lot
easier
for
someone
coming
in
looking
at
the
code
going.
Oh,
yes,
just
this
one
spot!
A
This
is
the
one
spot,
that's
unsafe
and
here's
why?
If
you
are
doing
functions,
you're
writing
code
and
someone
has
who's.
Calling
your
function
needs
to
uphold
a
very
specific
contract
themselves,
right,
like
oh,
don't
don't
pass
overall
pointer
in
or
don't
pass
a
null
pointer
in
or
whatever
the
case
might
be,
or
we
assume
that
this
is
a
null
terminated
string
right.
A
These
things
that
the
compiler
can't
check,
obviously
they're
at
runtime,
then
you
should
mark
the
function
itself
unsafe,
and
the
reason
being
is
that
you
want
other
people
when
they're
calling
that
function
to
know.
Yes,
this
is
fine
or
I
need
to
I
need
to
uphold
certain
laws.
They're
also
part
of
this
contract.
A
But
what
would
be
even
better
is
that,
instead
of
exposing
unsafe
interfaces,
you
actually
provide
safe
interfaces
on
top
of
them.
Nicks
is
a
wonderful
crate
as
an
example
for
this,
it's
a
wrapper
around
Lib
C,
and
not
only
does
it
deal
with
all
the
not
so
fun
stuff
with
error
handling
in
Lib
C,
but
it
also
provides
a
rust
like
interface
on
top.
So,
instead
of
dealing
with
like
I'm
writing
C
code,
that
just
happens
to
be
in
rust,
you're
dealing
with
I'm
writing
rusts
code.
A
It's
easy
to
go.
Oh
I!
Don't
need
to
write
documentation,
it's
fine
people
who
are
calling
this
will
understand
sure
for
regular
functions
may
be,
but
with
unsafe
functions.
You
know
it's
gonna
be
pretty
subtle
as
to
what
needs
to
happen
or
in
order
to
work
in
such
a
way
that
it
doesn't
break
people's
code
at
runtime
a
lot
of
the
time.
It's
you
can't
really
notice
that
until
it's
too
late
and
so
by
documenting
and
kind
of
putting
a
big
warning
sign
on
it,
people
can
go.
A
Oh
okay,
I
understand
what
I
have
to
do.
If
you
look
at
the
standard
library
Docs,
especially
under
STD,
D
mm
and
STD,
pointer
or
PTR
you'll,
see
that
there's
quite
a
few
functions
in
there
that
are
unsafe
and
they're
really
great
examples
of
this
they're
fairly
well
documented,
and
they
tell
you
all
the
things
you
should
do
in
order
to
make
sure
that
it's
safe
as
well
as
like
a
couple
of
gotchas.
If
you
do
it
incorrectly,
mem
transmute
is
a
very
particularly
big
red
warning.
Sign
of
don't
use
this.
It's
like
a
it's.
A
The
barrier
between
you
and
a
really
bad
time
is
just
a
little
bit
of
dental
floss.
It
is
not
much
and
but
that's
not
to
say
that
you
shouldn't
use
it.
Sometimes
it's
very
very
useful.
Unsafe
is
a
very
useful
thing
to
have,
and
sometimes
there
are
just
things
that
you
know
yourself
is
gonna,
be
fine
like
okay,
you
could
use
from
utf8
unchecked,
for
instance.
Well,
if
you're
directly,
writing
all
the
utf-8
bytes
into
an
array
and
it's
statically
there.
Well,
that's
fine!
A
You
know
it's
there,
so
you
can
just
use
that
you
can
use
it
unchecked
like.
Why
are
you
doing
a
compile
time
check
for
something
that
you
already
know
works
so
just
just
be
careful,
write
documentation
for
it
and
but
also
know
that
it's
not
a
bad
thing,
a
lot
of
times.
People
kind
of
look
at
unsafe,
as
this
like,
oh
I,
should
never
ever
write
it
ever
and
you
should
I
think
if
you
haven't
spent
some
time.
Writing
someone
safe
functions.
A
I
think
you
should,
but
if
your
first
instinct
too,
is
when
you're
writing
Russ
code
to
go.
Oh
the
compilers.
In
my
way.
Let
me
just
do
this
thing
that
I
did
or
whatever
in
an
unsafe
function.
You
probably
want
to
try
and
find
a
more
rust
like
way.
Usually,
when
the
compiler
is
telling
you
don't
do
that,
it
just
means
that
you're
not
really
kind
of
acting
within
rusts
design.
A
So
that's
kind
of
all
I
have
for
today
we
covered
a
lot
of
a
lot
of
things.
We
covered
new
types.
We
covered
the
Builder
pattern.
We
covered
kind
of
making
sure
that
you
turn
springle
type,
two
interfaces
into
types,
a
little
bit
of
unsafe
and
as
well
as
about
unwrap
and
expect
so
yeah.
Thank
you
very
much.