
Deriving Traits in Rust with Procedural Macros - naftulikay
https://naftuli.wtf/2019/01/02/rust-derive-macros/
======
cbrewster
Great article, there aren't many resources for writing proc macros right now.

However, I think this could be done with a generic impl of WritableTemplate
for all T where T: Template.

~~~
redshirtrob
Agreed. I was anxiously waiting for proc macros to land on stable for quite a
while and was very happy when they did.

But, when the time came to implement my custom derive I had to consult many
sources. I ended up piecing together what I needed from a combination of:

    
    
      - Official documentation (The Book)
      - Blog posts
      - Reading Serde code (And Syn/Proc-Macro/Proc-Macro2)
    

I also found the introduction of the proc-macro2 shim crate (however well
intentioned) caused quite a bit of confusion. Specifically, it wasn't clean if
I should use proc-macro or proc-macro2, and if I should be using the
TokenStream exported by the former or the latter. Or should I be using one in
some cases, and the other in some cases. Ditto for a few other things that I
just can't recall right now.

I did get things put together eventually, but I don't feel I understand things
well enough to explain to someone else...yet.

All that said, it's a hugely powerful feature and well worth the time if you
need to do things that require intimate knowledge of the AST.

------
reissbaker
I'm a Rust fan and procedural macros are legitimately a cool feature, but the
example seems much more simply solved via inheritance. You could do this
without procedural macros by using a default method definition on the
WritableTemplate trait, since WritableTemplate inherits from Template and
should have the render method in scope.

This could just be a slightly contrived example to show the neat kinds of
things you can do with procedural macros, though. If you needed a reference to
the original struct definition, for example, procedural macros allow you to do
the kinds of transformations at compile time that other languages need runtime
reflection for.

------
rhn_mk1
I'm a little confused about the statement that procedural macros are new in
the 2018 edition.

I've always stuck to what Fedora was shiping, and they seem to be shipping the
stable versions. Yet I used procedural macros back in 2016 already [0]. Does
my memory fail me, or is there some other change that happened now?

[0] [https://github.com/rhn/gpx-
rust/blob/master/gpx_debug/src/li...](https://github.com/rhn/gpx-
rust/blob/master/gpx_debug/src/lib.rs)

------
insertcredit
Rust is morphing into a complexity beast that rivals C++. When the cognitive
load require to read and write Rust code far exceeds that required of other,
more popular languages, the future does not look rosy.

~~~
bpicolo
Procedural macros are something you more or less never need to write in
application code, but they add tremendous power to libraries

~~~
lmm
Deriving typeclass implementations is something I do all the time in regular
application code (in Scala), once you're used to it it gives you a lot of
safety and expressive power. It sounds like Rust would benefit from some kind
of record system / generic representation of traits (like we get from
Shapeless in Scala) so that generic trait deriving could be written in normal
code without needing macros.

~~~
papaf
Isn't Shapeless based in macros?

That said, an equivalent library in Rust would be very useful.

~~~
lmm
Shapeless has one or two macros in its implementation, but as far as the rest
of the ecosystem is concerned it might as well be part of the language. The
point is that you can implement a custom typeclass and derivation of instances
of that typeclass for struct-like ((possibly recursive) compositions of)
sum/product types without ever having to write a custom macro.

------
sierdolij
Lifetimes - implicit/explicit semantics for how long a name is considered
alive, whereas in C++ there would be a delete or falling out of scope.

Borrowing - I still don't understand how or why a non-reference is implicitly
consumed by passing it (read-only intention) by value(?) to another function
and then can't be used again. Pony does explicit consumption.

There seems to be a need for training classes in Rust that explain the
development philosophy, because it's not readily apparent from the online
resources to anecdotal me who's able to code in Haskell, Elxir, Erlang,
Clojure, C, C++17, Ruby, Python, Go, assembly and LLVM IR.

~~~
throwmeawayjj
> I still don't understand how or why a non-reference is implicitly consumed
> by passing it (read-only intention) by value(?) to another function and then
> can't be used again.

By default the value is moved in Rust, just like with std::move in C++17,
which you say you know. This is to avoid performance issues when you pass
complex structures, such as vectors, around. If you want to copy your value,
you have to call .copy() explicitly.

~~~
eridius
Well, .clone()

