5 ms·
Deriving Traits in Rust with Procedural Macros
- insertcredit 8y agoRust 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.
- sierdolij 8y agoMaybe. It has a lot of practical innovations and constraints but some inconsistencies and rough edges that will likely be addressed. Perhaps programming languages need the freedom to try things, make mistakes and then use feedback with an RFC process, which Rust has, to make improvements. Feel free to submit RFCs if you notice anything specific.
- skywhopper 8y agoRFCs cannot unwind the complexity on display here in this article, unless the Rust community would entertain a proposal for removing procedural macros entirely, which I assume is a non-starter.
- wtetzner 8y agoThe procedural macro feature of Rust is very simple:you write code that reads a token stream and writes a new token stream. This article might have done something unnecessarily complex, but that doesn't mean the feature is complex.
- bpicolo 8y agoProcedural macros are something you more or less never need to write in application code, but they add tremendous power to libraries
- shmerl 8y agoWhich doesn't make it easier, since you might want to structure part of you application code as a reusable library.
- atoav 8y agoIt is entirely optional. I use Rust for two years now and work on a few things (biggest one around 10kLOC). I didn’t even read the Macro section in my books yet. Because I didn’t need to.
- keldaris 8y agoMost of the complexity of C++ is entirely optional too. That doesn't make the observation any less valid.
- lmm 8y agoDeriving 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 8y agoIsn't Shapeless based in macros? That said, an equivalent library in Rust would be very useful.
- lmm 8y agoShapeless 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.
- danieldk 8y agoThis may be true, but is not a very constructive comment without pointing out which language features or interactions between language features you find complex. Ownership and the borrows checker may have a steep learning curve, but are not very complex. The rules are quite simple, the learning curve is steep because most programmers do not typically think about ownership (though they should). (In my experience in teaching Rust, things like trait impl coherency rules, object safety, and finding a good balance between static and dynamic polymorphism are much harder for students than understanding the ownership system.)
- skywhopper 8y agoPresumably the comment is not about Rust ownership rules, but about the language feature on display in the article: procedural macros.
- TheCoelacanth 8y agoThe are considerably simpler than the equivalent feature in C++ (template metaprogramming).
- sierdolij 8y agoLifetimes - 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.
- bsder 8y agoI found that I actually needed to buy the "Programming Rust" book, read it cover to cover, and then go back and start programming. When I just tried to jump in, things didn't quite click.
- throwmeawayjj 8y ago> 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 8y agoWell, .clone()
- sierdolij 8y agoFirst: Grow up throw-away account, you don't need to add FUD know-it-all, jackassism. It's uncivilized and substance-free. You can sign up under a legitimate handle and behave along the intended guidelines, or go somewhere else. That explains the move but not mandating explicit verbosity for a common case. Pony, for example, has consume to end the lifetime of a variable, .
- 8y ago
- cbrewster 8y agoGreat 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.
- majewsky 8y agoYeah, I dropped out after three paragraphs thinking that the author is waaay overdoing it. If this is just a toy example, it should be labeled as such.
- atombender 8y agoThat bugged me, too -- if your entire article is about using a particular tool, then the reason for using that tool should be rock solid. I found the linked article [1] much more helpful in explaining procedural macros, even though it didn't offer many reasons to use them (other than linking to Serde etc.). [1] https://blog.rust-lang.org/2018/12/21/Procedural-Macros-in-Rust-2018.html https://blog.rust-lang.org/2018/12/21/Procedural-Macros-in-R...
- redshirtrob 8y agoAgreed. 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.
- rhn_mk1 8y agoI'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/lib.rs https://github.com/rhn/gpx-rust/blob/master/gpx_debug/src/li...
- reissbaker 8y agoI'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.