►
From YouTube: Nine Rules for Creating Procedural Macros in Rust - by Carl Kadie - Rust Linz, December 2022
Description
Practical lessons from anyinput, a new macro for easily accepting string/path/iterator/array-Like inputs (Based on my recently published free article in Towards Data Science)
Article: https://medium.com/towards-data-science/nine-rules-for-creating-procedural-macros-in-rust-595aa476a7ff
Slides: https://1drv.ms/p/s!AkoPP4cC5J641sMUBT9olBqrd-JoZw?e=kJ38eo
Rust Linz:
https://sessionize.com/rust-linz
https://rust-linz.at
https://fosstodon.org/@rustlinz
https://twitter.com/rustlinz
A
Thank
you
so
I'm
going
to
talk
about
creating
procedural
macros
in
Rust.
These
are
lessons
I
learned
when
I
created
a
rust
Macro
for
myself,
maybe
of
interest
to
other
people
called
any
input
and
we'll
use
that
macro
as
an
example
we'll
talk
about
it
more,
but
it's
basically
to
make
it
easier
to
write
functions
that
can
be
permissive
in
what
kind
of
inputs
they
allow.
A
So
by
way
of
introduction,
11
years
ago,
I
was
working
as
a
developer
at
Microsoft
research
in
the
Seattle
area,
and
we
created
a
genomics
package,
cost
called
fastlim
that
we
open
sourced
that
was
written
in
mostly
python,
with
some
of
the
performance
pieces
written
in
C
plus
three
years
ago,
I
retired
and
began
work
as
an
open
source
volunteer
continuing
to
maintain
the
fastlim
project.
Most
of
that
work
involved.
Things
like
updating
the
python
from
version
2
to
version
3
and
just
kind
of
keeping
up
with
some
user
feature
requests.
A
Two
years
ago,
I
finally
just
became
fed
up
with
maintaining
the
C
plus
plus
part
of
the
code
and
replaced
it
with
rust,
and
that
was
successful
for
the
project
and
it
also
led
to
an
article
called
Nine
rules
for
writing.
Python
extensions
in
Rust
summer
I
pulled
out
a
piece
of
the
genomics
package
that
reads
a
particular
genomics
format
called
bed
and
made
that
available.
It
was
already
written
in
Rust,
but
it
wasn't
easy
to
use
from
rust.
A
It
was
made
to
be
easy
to
use
from
Python
and
I
wanted
to
see
if
I
could
make
an
API
in
Rust.
That
was
as
good
as
what
I
was
able
to
make
available
in
Python
I.
Think
I
was.
It
was
more
work
to
do
it
in
Rust,
but
I
think
for
users,
it's
just
as
usable
as
Python,
and
that
became
an
article
called
Nine
rules
for
elegant
rust,
Library
apis.
A
Finally,
this
fall
I
simplified
the
rust
code
with
this
new
any
any
input
macro
and
along
with
this
talk,
you
can
get
all
the
details
from
an
article
on
medium.com
specifically
towards
data
science
called
Nine
rules
for
creating
procedural
macros.
In
Rust,
so
let's
talk
about
the
any
input
macro
that
I
wanted
to
create
and
I'll
start
with
a
question
that
everybody
can
kind
of
ask
themselves
if
I.
A
If
you
were
writing
rust
and
I,
ask
you
to
write
a
function
that
accept
that,
except
that
any
kind
of
string
like
thing
would
you
know
how
to
do
that?
In
other
words,
instead
of
just
taking
a
stir
or
a
string,
you
could
do
a
borrowed
or
moved
there's
also
references
to
Strings
that
can
be
borrowed.
Could
you
make
a
function
that
could
take
any
kind
of
string,
so
your
user
wouldn't
have
to
match
your
kind
of
string.
A
I
bet
I
can
kind
of
remember
how
to
do
that.
So
that's
not
the
hardest
thing
in
the
world
to
do
in
Rust.
But
what,
if
I
asked
you
to
do
the
next
thing,
which
is
to
ex
write
a
function
that
it
could
accept
as
input
any
iterator
like
thing
where
each
component
of
the
iterator
was
any
path-like
thing,
it's
getting
more
complicated,
I'm
sure
some
people
can
do
it.
This
is
I'd
have
to
look
this
up
every
time.
A
I
would
do
it
and
then
the
final
challenge
here
is
what,
if
you're
using
the
multi-dimensional
array
package
ND
array,
but
you
want
to
accept
a
normal
rust
Vector
of
f-32s.
Would
you
know
how
to
have
a
function
that
could
take
a
vector
of
f32s,
but
inside
your
function
it
would
look
like
an
ND,
one-dimensional
array,
View
and
I
didn't
even
know
that
was
possible
for
most
of
the
time.
The
point
of
this
macro
is
to
make
that
easy,
and
we
can
look
at
an
example
on
the
left,
starting
at
line
three.
A
We
can
define
a
function
that
uses
this
macro.
We
say
pound
any
input.
We
Define
a
function
called
length
plus
two.
It
has
an
input
called
s
which
is
of
type
it's
not
a
real
type.
It's
our
special
type
called
any
string
and
it
returns
a
u
size
and
then
inside
our
function
we
just
say
s
dot,
length
plus
two,
that's
what
our
our
function
does
and
you
may
not
believe
that
this
can
take
any
kind
of
string,
but
you
can
see
starting
lines:
10
through
18.
A
I'm,
passing
in
strings,
stirs
references
to
Strings
and
sometimes
they're
barred
and
sometimes
they're
moved.
So
that
demonstrates
what
the
macro
can
do
for
Strings
and
on
the
right
side.
We've
got
a
more
complicated
example.
Now
we'll
say
we
want
to
define
a
function
called
to
it
or
sum
its
first
input
is
called
enter.
One
hitter
one
is
any
kind
of
iterator
of
you
size
and
iterator.
A
2
is
any
iterator
of
any
string,
light
thing
and
the
function
returns,
a
u
size
and
then
inside
the
function
we're
going
to
sum
up
the
numbers
we
get
in
the
first
iterator
and
in
the
second
iterator
we'll
take
the
length
of
all
the
string
like
things
and
we'll
add
that
to
the
to
our
are
accumulating
sum
and
then
we'll
output
it
and
at
line
13
on
the
right.
You
can
see
an
example.
A
We
call
two
iterator
sum
on
an
interesting
iterator,
an
iterator
where
we're
iterating
from
1
to
10,
inclusive
and
then
also
an
array
of
strings
or
stirs
or
something
like
that,
and
that
works
fine
and
we
can
call
the
function.
It'll
return
the
correct
answer,
which
in
this
case
is
61.,
so
that's
what
the
macro
I
wanted
to
create
and
have
created
does.
A
So
how
do
we
create
a
macro
like
that?
The
simplest
kind
of
rust
macro
is
called
the
declarative
macro
with
that
you
use
the
macro
rules
macro
to
create
your
macro
when
you
can
use
it.
This
is
the
right
thing
to
use
it's
simple:
to
use
easy
to
read,
but
not
as
powerful
as
I
needed
for
this
for
this
project.
A
A
The
attribute
macro
is
the
one
we'll
be
using.
That's
where,
like
on
the
top
of
a
function,
you
can
say
pound
square
bracket
and
the
name
of
your
macro
and
that'll
cause
your
macro
to
be
called
on
all
these
procedural
macros.
What
happens?
Is
you
write
your
code
one
way
and
before
the
compiler
actually
compiles
your
code?
It
sends
a
token
string
of
the
code
that
you
wrote
into
a
function
that
that's
the
main
thing
you're
going
to
have
to
write.
Your
function
is
going
to
return
a
token
string
whatever.
A
That
is
we'll
see
that
in
a
bit
that
you
can
think
of
it
as
ex
as
your
original
thing,
expanded
or
modified
transformed
some
way
and
then
that
second
thing
after
it's
run
through
your
macro
function,
will
be
what
the
compiler
actually
compiles.
A
So
I
could
have
called
this
talk.
Nine
suggestions
for
making
procedural
macros,
but
if
you
call
something
just
suggestions,
it's
kind
of
boring,
so
I'm
going
to
call
them
rules,
but
that
doesn't
mean
that
I'm
really
the
authority
on
this
and
that
you
have
to
do
everything.
I
say
there
really
are
suggestions,
but
I
think
it's
just
less
wishy-washy
for
me
to
be
a
little
Bolder
and
and
call
them
rules.
So
we're
going
to
look
at
the
rest
of
the
talk
will
be
one
slide
per
rule
for
the
nine
rules.
A
Four
of
the
rules
will
relate
to
syntax
trees,
how
to
convert
among
the
various
types
related
to
syntax,
how
to
learn
about
the
syntax
tree
data
structures
that
you'll
need
to
work
with
how
to
construct
new
syntax
trees
for
your
output
and
when
you
need
to
recursively
manipulate
syntax
trees.
How
to
do
that
in
a
way?
That's
not
too
terrible.
A
The
there'll
also
be
five
rules
related
to
other
topics,
including
how
to
develop
your
project
as
an
your
macro
project
as
a
non-macro
project,
it
sounds
a
little
strange,
but
it
turns
out
that
makes
things
much
much
easier
how
to
create
unit
tests
that
you
can
actually
debug
how
to
do
error
handling
how
to
do
integration
tests,
including
UI
tests,
and
the
importance
of
following
the
rules
of
just
general
elegant,
rust,
API
design,
foreign.
A
A
There
will
be
a
top
level
project,
it'll
be
very
small.
Most
of
it
will
just
be
your
documentation.
The
only
important
part
of
it
is
to
re-export
a
function,
that's
defined
in
the
next
level
project.
A
A
That
will
be
a
function
that
in
this
case,
any
input
that
takes
some
token
stream
input
or,
in
this
case
inputs
and
returns
a
token
stream
in
my
code
for
this
level
and
I
and
I
recommend
this
I
only
have
11
lines
of
code
because,
what's
really
going
on
is
I
Define
this
function,
any
input
and
all
it
does
is
call
any
input.
Underscore
core
passes
the
two
inputs
to
it
and
then
takes
the
output
from
any
input
core
and
and
returns
it
and
you'll
notice,
some
in
twos.
A
That's
because
there's
actually
two
kinds
of
token
streams
that
are
designed
to
be
very
similar
and
Inter
convertible
and
doing
those
dot.
In
twos,
what's
the
tells
the
compiler
that
it
can
convert
them
for
us,
so
we've
had
two
levels
that
have
done
almost
nothing.
The
core
project
is
where
the
real
work
gets
done.
We
Define
a
function
in
this
case,
I
called
it
any
input
core.
It
takes
some
inputs
of
type
tokens
during
and
returns
a
token
string.
A
Now,
we'll
actually
write
a
bunch
of
code
that'll
do
the
conversion
of
the
of
the
code
that
we
want,
so
the
user
will
give
us
and
Mike
in
the
any
input
case
a
function,
and
we
will
see
this
in
a
bit
and
then
we'll
return
the
modified
function,
and
we
can
have
lots
of
other
code
for
helper
functions
and
all
kinds
of
data
structures
and
unit
testing.
A
So
you
may
think
it's
confusing
to
have
to
have
three
rust
projects.
When
you
really
only
wanted
to
write
one
anytime,
you
do
macros
you're
always
going
to
have
to
have
two.
The
top
two
I
have
here.
I
strongly
recommend
adding
the
third,
because
it
makes
debugging
much
easier.
It
allows
you
because
the
core
project,
the
bottom
one,
is
a
non-macro
project.
A
You
can
do
things
like
set
breakpoints
I
use
vs
code,
so
you
can
set
a
point
in
your
code
and
vs
code
as
a
breakpoint
runs
line
by
line
single
line
at
a
time.
Look
at
your
variables,
just
all
the
normal
stuff
you
would
do
with
coding,
becomes
available
at
this
lower
level
core
project,
it's
not
available
at
the
derived
project,
and
that's
why
we
keep
the
derived
project.
A
So
small
one
help
with
having
to
have
a
thing
that
you
think
of
as
one
project
be
three
projects
is
rust,
has
a
feature
called
workspaces
where
you
can
tell
it
that
these
three
projects
are
coordinated
and
work
together,
and
if
you
read
the
article
it'll
talk
about
how
to
set
it
up.
These
three
has
a
single
rust
workspace,
and
so
that
means
the
overhead
of
having
three
projects
isn't
isn't
too
bad.
A
So
we
rule
two:
is
we
want
to
to
create
a
procedural
macro?
You
need
to
be
able
to
freely
convert
among
between
token
streams,
syntax,
trees,
strings
and
literal
code,
and
we
haven't
defined
token
streams
and
we
don't
know
what
syntax
trees
are
and
I
guess
we
know
what
strings
are.
Let
me
give
an
example.
A
So
look
at
line
five
that
this
is
a
first
implementation
of
the
any
input
function
in
our
at
our
core
level.
It
has
the
first
input
args
of
type
token
string,
we're
not
going
to
use
that
input.
The
second
input
is
called
input.
A
It's
of
type
token
stream,
that'll
be
a
tokenized
version
of
the
user's
in
this
case
function
and
we're
going
to
take
that
and
at
line
six
we're
going
to
call
the
parse2
function
and
tell
it
that
we'd
like
it
to
convert
the
token
stream
called
input
into
a
syntax
tree,
object,
called
item,
FN
item
function
and
that'll,
be
called
Old
item
be
assigned
to
a
variable
old
item
function.
Then
we're
going
to
put
that
old
item
function
into
a
function
called
transform
function,
which
you
can
see
at
line.
11.
A
A
The
first
thing
it
will
do
is
print
whatever
the
the
item
function
is,
and
we
can
do
that
with
print
line,
and
this
quote
macro
with
that
pound
sign
it's
kind
of
a
little
magic
syntax,
but
if
we
do
that
which
we
do
is
a
debugging
like
during
development
while
we're
developing.
If
the
user
passed
in
a
function
called
Hello,
Universe
print,
Hello
Universe,
you
can
see
it
down
below
in
the
white,
it
would
print
the
string.
Hello
Universe
function,
Hello
Universe
print,
Hello
Universe.
A
We
can
also
see
this
item
function
thing
as
what
it
is
and,
and
it's
a
piece
of
the
syntax
tree
all
the
syntax
tree
is-
is
a
nested
structure
of
structs
and
enums
that
are
defined
in
a
a
crate
called
sin
s-y-n.
A
So
if
we
told
it
to
print
the
syntax
tree,
it
would
start
by
printing
what
you
see
in
the
lower
line
of
the
white
item
function
and
you
can
see
that's
a
struck
and
the
struck
has
an
attribute
called
att-r-s,
which
is
an
empty
list
array
and
it
has
a
field
called
Vis,
which
has
some
value
inherited,
we'll
we'll
see
how
we
can
learn.
What
these
are
has
something
called
Signature
and
and
so
on.
A
That's
a
way
that
to
capture
literal
code,
which
is
lines
15,
16
and
17,
and
turning
it
into
an
item
function,
and
it
knows
which
item
function,
because
it
knows
the
return
type
of
transform
function
anyway.
Think
of
it,
this
way.
There's
these
four
kinds
of
things:
token,
streams,
strings,
syntax,
trees
and
literal
code,
and
we
might
want
to
convert
between
any
of
them.
A
There's
functions
to
convert
every
which
way
you'd
want
they're
a
little
bit
hard
to
figure
out
a
little
bit
hard
to
remember,
but
you're
in
luck,
because
I
made
a
table,
so
here's
my
on
the
right,
you
can
see
my
table
of
16
combinations
of
things
that
you
might
want
to
convert
to
and
from
and
what
the
exact
rust
syntax
is
to
do
them.
So
this
is
in
the
article
I'd
remember,
recommend.
Keeping
this
at
hand.
I
was
using
it
all
the
time.
A
So
from
Rook
2
we
now
hopefully
have
some
ability
to
convert
among
all
the
types
that
we
have
to
deal
with.
We
want
to
create
start
yeah,
we'll
say
we're
going
to
start
in
kind
of
a
test
driven
manner,
so
we
want
to
create
debuggable
unit
tests
and
let's
look
at
an
example
of
what
one
of
those
would
look
like.
This
is
just
in
our
lowest
level
core
project.
A
We
Define
a
test
just
like
a
normal
rust
test.
This
one's
called
one
input
and
the
first
thing
we
do
in
line
65
through
68
is
we
show
what
a
user's
input
might
look
like
before
we
apply
our
macro,
so
the
user
might
have
something
called
any
string
length
that
takes
its
input,
s
of
type
of
the
pseudotype
any
string
and
returns
a
a
u
size.
A
So
we
call
that
before
we
also
starting
in
line
71
show
it
what
we
want
the
what
we
expect
the
function
to
be
converted.
What
do
we
expect
our
macro
to
convert
this
function
into?
So
you
can
see
it's
still
a
function
called
any
stir
line,
but
now
it's
got
a
generic
type
called
any
string.
Zero
and
that's
now
the
type
of
our
input,
the
actual
type
and
there's
a
where
Clause
that
says
that
any
string
zero
is
specifically
an
azeref
stir
and
also
line
76.
A
You
can
see
that
we
expect
the
Mac.
We
expect
that
the
macro
will
add
a
new
line.
It'll
say
s
equals
s
dot
as
ref
and
then,
after
that,
the
rest
of
the
code
is,
should
stay
the
same
as
it
was
before.
So
we
have
our
before
code,
our
expected
code
at
line
81.
We
just
pass
in
our
before
code
to
our
our
main
core
function.
A
Mine's
called
any
input
core
and
get
the
after
code,
that'll
return
token
strings,
and
then
we
can
just
assert
that
the
token
strings
we
expected
the
ones
are
expected
are
the
same
as
the
ones
that
we
actually
got
in
after
and
any
tokens
EQ
is
a
little
function.
I
created
you
can
get
it
from
GitHub
or
from
the
article.
A
All
it
does
is
make
sure
that
the
two
things
are
equal
and
if
they're
not
equal,
it
does
a
line
diff
to
help
you
figure
out
where
something
went
wrong
and
then,
finally,
on
these
unit
tests
that
usually
take
another
copy
of
the
after
code
and
just
run
it
on
something.
So
here
I'm
taking
any
string
length
and
applying
it
to
the
string,
ABC
and
verifying
that
it
equals
three
and
that's
just
to
make
sure
that
this
thing
is
doing
about
what
we
want.
A
The
nice
thing
about
putting
this
in
the
core,
the
lowest
level
project
is
again.
We
can
set
break
points.
So
if
something
isn't
working,
we
can
set
a
break
point
at
line
82
and
then
sorry
line
81,
where
we
call
any
input,
one
any
input
core
and
we
can
actually
watch
the
code.
We
can
go
into
the
code
and
watch
it
execute
line
by
line
look
at
variables
and
that
makes
debugging
much
much
easier.
A
So
one
of
the
hardest
things
about
writing
procedural
macros
is
that
there
is
this
huge
number
of
enums
and
structs
that
are
used
in
the
syntax
tree
format
and
it's
not
immediately
obvious
how
to
learn
how
to
use
them.
So
here's
my
recommendation,
take
a
function
that
you're
curious
about,
for
example,
what's
in
the
black
rectangle
at
the
top
and
then
go
to
AST
Explorer,
which
is
a
website
abstract,
syntax
tree
Explorer
make
sure
you
set
it
to
rust.
A
When
I
first
went
there,
I
only
saw
the
JavaScript
version
and
I
got
very
confused,
but
there
is
a
rust
option
there.
You
can
paste
your
little
function
into
AST,
Explorer
and
it'll
output.
What's
in
the
gray
box
on
the
left,
in
this
case,
we
can
see
that
it
sees
a
file
and
some
things
and
then
an
item
FN
and
we
think,
oh,
maybe
the
rust,
syntax,
free
representation
for
a
function
is
an
item
FN.
What's
an
item
FN
well,
it
looks
like
it's
a
struck
with
three
Fields,
a
ttrs.
A
This
sig
and
block
Sig
itself
is
of
type
signature.
Signature
is
some
kind
of
struck
with
a
bunch
of
inputs,
so
that
gives
you
just
some
clue
that
you're
in
my
case,
that
I'm
interested
in
the
syntax
tree
struct
types
of
like
item
FN
and
signature.
A
A
If
it
does
have
a
where
Clause
I
needed
to
return
it
to
me
and
if
it
doesn't
have
the
WHERE
Clause
I
need
to
create
the
an
empty
version
of
the
contents
of
the
where
Clause.
So
you
can
see
line
three
on
the
right
side,
that
I
take
the
item,
FN
input
and
say
dot
Sig.
That
gets
me
to
a
particular
field.
A
Dot
generics
gets
me
to
a
subfield
dot
where
Clause
gets
me
to
another
subfield
and
then
do
a
rust
pattern
matching
to
see
if
that,
where
Clause
is
actually
there
and
if
it
is
I
pull
out
one
of
its
parts
called
predicate
and
if
it's
not
I
use
the
parse
quote
that
we
saw
from
rule
2
to
create
an
empty,
basically
an
empty
predicate
structure,
which
is
the
you
can
see
the
return
type
line,
two
it's
something
called
out
punctuated
where
Clause
comma
and
again.
A
We
know
that,
because
we've
used
the
AST
Explorer
to
find
out
what
the
what
types
were
expected.
A
So
now
we
have
at
least
a
handle
on
passing
in
the
user's
thing,
in
my
case,
a
function
knowing
how
to
con
converting
it
into
a
syntax
tree
being
able
to
see
what
kind
of
structs
and
enums
we
expect
the
syntax
tree
to
contain.
A
We
might
get
to
a
point
where
we
want
to
actually
generate
some
new
code,
that's
different
from
the
user's
old
code.
The
simplest
way
to
do
that
when
and
should
definitely
be
done
when
it
works,
for
you
is
to
use
the
parsequote
macro
and
another
important
way.
That's
not
too
hard
is
with
rust's
update
syntax.
A
So
first
the
parse
quote
in
my
macro
I
need
to
create
a
line
of
code.
It
will
be
the
first
statement
and
it'll
become
the
first
statement
in
the
user's
function.
We
saw
the
case
where
I
had
s
was
in
any
string
and
I
wanted
it
to
say
s
equals
s,
let
s
equal
s,
dot
as
ref.
A
So,
on
line
one
we're
passing
in
an
identifier
in
the
scenario
and
talking
about
it
would
be
S
the
variable,
the
input,
the
variable
the
letter,
the
user
is
using
for
input
and
you
can
see
lines
four.
Five
and
six
is
where
it
generates.
This
new
statement,
that's
going
to
become
s,
equals
s,
dot,
azuref,
and
you
can
see
it's
got
this
kind
of
substitution
syntax
with
the
pound
sign
that
actually
makes
it
pretty
fun
to
use
in
the
case
of
iterators,
We
Still
generate
a
statement,
but
it's
a
little
bit
different.
A
It
would
be
like
iterator
one,
let
iterator
one
equal
iterator
one
dot
into
iterator
and
in
the
case
of
my
multi-dimensional
arrays,
it's
yet
another
line
that
we
would
want
to
add.
But
in
all
the
cases
it's
the
parse
quote
makes
it
pretty
fun
to
generate
new
code
and
we
can
put
in
our
own.
We
can
substitute
in
our
own
values.
The
other
straightforward
way
of
generating
code
is
when
you
have,
you
want
to
generate
a
new
struct.
A
So,
on
the
right
side,
I
want
to
generate
a
new
item
function
so
I
say:
okay,
I
want
a
new
item
function.
The
first
field
is
Sig
and
then
I've
got
some
expression
that
tells
how
I
want
to
set
that
field.
The
second
field
is
block
and
I
have
got
some
expression
to
say
how
to
set
that
field.
It's
got
two
more
Fields
I,
don't
want
to
change
them.
A
I
want
them
to
be
exactly
what
they
were
before
so
just
standard
rest
feature
is
that
you
can,
when
you're
filling
in
a
struck,
you
can
say
dot
dot,
some
other
structure,
it's
the
same
type
in
this
case
clone
and
it'll
just
fill
in
the
last
two
fields
of
my
new
item
function
that
it's
creating
with
whatever
values
it
had
from
old
FM
at
line
12..
A
If
you've
only
got
four
Fields,
then
maybe
it's
not
that
important
you
could
have
just
I
could
have
just
specified
all
four.
But
if
we
look
at
line
I
think
line
three
signature
I'm,
creating
a
new
struct
of
type
signature,
I
set
the
generics
field
to
something
that
I
know
I
want
it
to
be
I
set
the
inputs
field
to
be
what
I
want
it
to
be,
and
then
I
say
the
rest
of
the
fields
should
just
be
the
same
as
they
were
before.
A
That's
line
six-
and
this
saves
me
a
lot
of
typing,
because
I
think
signature
has
nine
fields.
So
this
was
just
a
nice
standard
rest
way
of
creating
new
structures
based
on
Old
structures
and
not
having
this
specify
every
single
field.
Explicitly.
A
So,
for
example,
we've
seen
things
where
our
user
might
have
said
had
a
function
with
an
input.
S
is
any
string.
Okay,
that's
not
that
complicated!
You
could
also
have.
They
could
also
say:
V
is
a
vect
of
any
path.
It's
like
yeah,
okay,
that's
a
little
more
complicated.
Now
our
special
pseudotype
is
inside
a
standard
type
effect
or
we
could
have
a
is
in
any
array
of
any
paths.
So
now
we've
got
two
of
our
pseudotypes
nested,
it's
getting
more
complicated.
A
Even
worse,
say
they
have
a
a
variable
called
yikes
and
it's
type,
it's
a
pseudotype
special
I'll,
say
pseudotype
any
itter
where
each
element
is
a
pack
where
each
element
of
the
vect
is
any
array
where
each
element
of
the
array
is
any
path,
and
you
can
just
look
at
that
and
see
that
you're
going
to
have
to
use
recursion
to
handle
cases
like
this.
So
the
good
news
is
the
scene:
crate
provides
on
a
nice
mechanism
for
doing
all
the
recursion
that
we
need
for
both
visiting
and
creating
manipulating
syntax
trees.
A
Recursively,
it's
all
attached
to
the
fold
crate
it
the
way
it
works.
Is
you
tell
it
to
start
recursively
processing,
some
syntax
tree
and
then
for
all
the
types
that
you
want
to
do
something
special
with,
and
it
knows
about
a
it
knows:
there
are
187
syntax
tree,
structs
and
types
the
ones
you
care
about.
You
tell
it
to
do
something
special
for
those
for
those
types,
the
ones
you
don't
tell
it
about.
It'll
do
nothing
as
as
a
default.
A
Sometimes
when
you're
doing
the
recursion.
Your
type
in
my
case,
something
you
want
to
manipulate,
might
itself
be
built,
have
subparts
that
are
also
something
you
want
to
manipulate.
A
So
you
have
to
do
recursion
within
recursion
nested
types.
It
can
still
handle
that.
But
you
have
to
be
careful
that
you
set
that
up
the
right
way
or
you'll
miss
the
inner
one
and
that's
too
complicated
to
explain
now,
but
look
at
the
article,
it's
not
too
bad.
It's
just
a
special
line
that
you
have
to
put
at
the
right
place.
A
Okay,
that
was
all
I,
think,
that's
all
the
programming
inner
programming
syntax
tree
stuff.
So
let's
talk
about
error,
handling,
rule
7
is
use
the
proc
macro
error,
crate.
A
All
rust-coded
needs
to
handle
errors.
Of
course.
Sadly,
macros
don't
use
the
rust
result,
types
that
we're
used
to
in
regular
coding.
A
We
know
that
we
don't
use
results.
We
can
often
just
call
Panic
which
isn't
as
good
as
results,
but
is
something
that
kind
of
works
with
macros,
but
not
perfectly
because
when
we
return
an
error
message
in
a
macro,
we
want
to
be
able
to
point
to
the
part
of
the
user's
code
that
caused
the
error.
We
want
a
location
and
if
we
call
a
panic
like
we
would
in
normal
code
it
won't
have
the
location
information.
A
There
are
a
couple
efforts
to
improve
this
situation,
but
the
within
the
within
proper
formal,
official
rust
but
they're
either
in
nightly,
and
they
don't
seem
to
be
working
their
way
out
very
quickly
or
they
only
work
as
on
your
top
level
function.
They
aren't
something
that
you
could
call
when
you're
recursed
four
levels:
deep,
parsing,
the
user's
code.
A
So
the
good
news
is,
someone
has
created
a
third-party
crate
called
proc
macro
error
and
it
does
exactly
what
we
want.
It's
just
like
Panic,
except
we
say
abort
and
we
can
give
a
location
the
downside
to
it
is
that
it
brings
in
some
overhead
and
makes
it
slower,
I
think
it's
more
than
worth
it,
because
it
does
exactly
what
we
want
and
it's
also
trying
to
be
compatible
with
the
way
that
things
might
officially
get
standardized.
A
So
you'll
only
have
to
make
little
changes
going
forward
if
the
error
handling
in
macros
story
matures.
A
So
here's
an
example
inside
my
macros
code
I
checked
to
see
whether
the
user,
when
I
have
nested
pseudotypes,
they
should
be
using
the
ankle
bracket.
So
like
any
interrupt,
any
string
should
have
an
angled
bracket.
If
the
user
does
a
round
bracket,
I
will
detect
that
in
this
function,
at
the
top
and
I'll
I
just
call
abort
span
range
is
something
that
I
was
able
to
easily
bring
in
from
the
user's
code
to
say
which
code
it
caused.
A
A
We
were
calling
our
function
that
I
that
the
macro
calls,
but
we
weren't
really
really
depend
seeing
what
happens
when
the
real
compiler
calls
our
macro
before
doing
its
compilation
to
do
that
kind
of
testing.
You
have
to
do
it
at
the
at
the
topmost
level,
and
you
and
you
should
do
that.
Here's
an
example:
that's
just
a
top
level
test.
We
have
a
test
called
one
input.
A
It
has
a
function
inside
it
where
we
Define
any
string
length
with
s
being
of
my
pseudotype
any
string
and
it
just
Returns
the
length
and
we
just
assert
that
the
any
string
length
of
one
two
three
in
quotes
is
equal
to
three.
So
that's
a
very
nice
integration
test.
The
compiler
is
really
running
the
macro
and
everything
is
is
good
there.
The
only
problem
is
along
with
testing
that
it
works.
Well.
A
We
also
need
to
test
that
it
when
the
user
makes
a
mistake,
that
it's
giving
the
right
error
messages
and
to
do
that,
we
create
tests,
we'll
call
UI
tests
that
use
the
tri-built
crate
the
way
the
tri-build
crate
works
and
people
use
it
for
other
things,
too,
is
you
create
little
bits
of
rust
code
in
a
directory
like
a
UI
directory
shown
on
the
lower
line,
four,
so
by
test1.rs
test2.rs,
whatever
you
want,
the
tri-build
will
find
your
files
run
them
through.
A
It
won't
know
what
to
expect,
but
it'll
output,
what
standard
error
it
actually
saw,
and
if
you
can
look
at
that-
and
if
you
like
it,
you
can
put
it
in
the
right
directory
and
then
from
then
on
it'll
check
that
it
is
consistently
creating
the
same
error
messages
when
it
that
it
should
be
creating
all
this
testing
at
multiple
levels
seems
complicated.
A
But
if
you
set
things
up
with
the
workspace
mechanism
I
mentioned
before,
then
you
can
run
all
your
low-level
unit
tests
and
all
your
integration
tests,
just
by
saying
cargo
test
underscore
underscore
workspace.
A
Okay,
our
final
rule,
the
final
rule
is
to
follow
the
rules
of
elegant
rust,
API
design
that
had
nine
more
rules.
The
three
most
important
ones,
though,
though,
are
to
use
Clippy,
which
is
the
linter
that
checks
that
you're
kind
of
doing
things.
A
Nice
you've
got
your
capitalization
of
variables
right
and
all
that
you
should
make
sure
you
write
good
documentation
because
you're
designing
something
for
other
people
to
use,
and
if
you
can't
explain
how
to
use
it,
then
maybe
your
design
isn't
good
and
you
should
test
your
design
on
your
own
projects.
What
we
call
eating
your
own
dog
food,
so
I
had
two
rust
crates
that
I
tested
this
on
the
any
input
macro
in
I
have
one
called
Fetch
data,
which
downloads
and
caches
big
sample
files
from
the
internet.
A
It's
compatible
with
Python's
popular
Pooch
package.
That
was
just
so
when
a
user
gave
me
a
path
of
something.
I
could
be
a
string
or
a
path
or
a
reference
to
a
path
that
worked
fine.
But
that
was
a
very
simple
case.
The
more
complicated
case
was
a
bed
reader
where
I
actually
had
iterators
of
strings
and
things
like
that
and
when
I
first
did
it.
A
I
was
surprised
because,
when
I
looked
at
the
documentation
that
bed
reader
generates,
it
turns
out
that,
for
all
these
procedural
macros,
the
documentation
uses
the
the
the
version
of
the
functions
are
data
structures
after
the
macro
has
run
so
and
so
I
had
a
function
in
bed.
Reader
called
chromosome
that
passed
in
an
iterator
of
any
kind
of
string,
but
the
documentation
actually
shows
the
generic
types
that
the
macro
generates
and
the
first
time
I
did.
A
This
I
was
using
types
like
t0
T1,
and
that
was
hard
for
the
user
to
read
and
might
conflict
with
their
own
generic
types,
and
then
I
did
big
long
random
types,
so
there
wouldn't
be
any
chance
of
conflict,
and
that
became
too
hard
to
read,
and
so
I
ended
up
with
this,
where
I
created
generic
types
like
any
string,
one
any
iterator
any
string
zero,
any
iterator
one
which
seemed
like
the
I'm
satisfied
with
with
this
design
and
the
reason
I
found
it
is
because
I
actually
applied
my
macro
to
a
real
project
and
got
to
and
got
to
exercise
it
okay.
A
So
this
is
my
last
slide.
The
conclusion
is:
if
you
follow
these
nine
rules,
you
can
make
writing
macros
as
easy
as
easy
as
writing.
Regular
rust
programs,
regular
rust
programs
that
manipulate
large
nested
structures,
so
it's
kind
of
easy,
but
still
not
super
easy.
The
plus
is
that
with
procedural
macros,
you
can
do
almost
anything.
If
there's
we
many
of
us
love
rust.
If
there's
anything,
you
think
could
be
better,
you
can
actually
make
it
better
yourself.
A
If
there's
any
redundancy
you
see
in
your
own
code,
just
because
of
your
project,
you
have
a
chance
to
remove
that
redundancy
to
make
the
language
more
productive.
For
you,
the
minus
is
that
some
things
seem
hard
when
you're,
creating
procedural
macros,
debugging,
testing,
error,
handling,
understanding,
syntax
trees,
I
think
all
those
are
made
easier.
If
you
follow
these
rules
which
require
you
to
set
things
up
right
and
things
do
get
easier
and
become
easy
under
these
under
these
rules,
and
they
really
does
allow
you
to
Unleash
Your
productivity.