Hacker News new | past | comments | ask | show | jobs | submit login
A Quick Intro to Rust Macros (danielkeep.github.io)
128 points by untothebreach on Oct 29, 2014 | hide | past | favorite | 38 comments



I think it's prudent to reinforce that macros in Rust are currently very wobbly. It would be an epic folly to stabilize them anytime soon, which means that any forthcoming stable Rust 1.0 release won't feature user-defined macros (stdlib macros like `println!` and `regex!` et al will still be available in 1.0, because only their interface needs to be stabilized, not their definition mechanisms). You'll have to stick to the nightly releases if you want to define your syntax extensions and such, at least until we straighten out this towering pile of hacks.

That said, we're looking at interim ways of providing some sort of relief for people who want to metaprogram in Rust 1.0. There aren't any concrete plans yet, but we love the idea of user-defined macros and we're not going to leave people out in the cold if we can help it.

(And perhaps you're wondering why we don't delay Rust 1.0 until macros are ready? Because the rest of the language works (or very soon will work) just swimmingly, and it doesn't make sense to deny stability from people who can live without macros (whose definition mechanisms can be introduced without breaking backwards compatibility). Rust 1.0 will be only the beginning of a gradual transition towards ecosystem stability, not a sudden leap.)


I would assume that there will be a huge temptation to rewrite the standard library once macros are a normal language feature. For the language macros might be purely additive, but for the API designer it is a big difference.


Given that Rust developers have had macro access for a long time now, I don't think that's true. Or at least, I don't think that the standard library would be any different if macros were stable. Something like HKT would, though, and is already being considered with regards to the standard library's design.



This clarification is very much appreciated, thank you :)


Very cool stuff. I started to play with Rust some more and started to really like it. Haven't gotten to macros yet.

I've been reading the official guides. Those helped clear up some difficult points for me: lifetimes and interfacing with lower level code.

http://doc.rust-lang.org/#guides

Also in what seems like a very short time, Rust community addressed the entries in Shootout Computer Benchmark game. Those are toy scores and one should not take them seriously. But unfortunately people do. So they made those fast.

http://benchmarksgame.alioth.debian.org/u64q/code-used-time-...


"Those are toy scores and one should not take them seriously."

People keep saying that about The Shootout, but I don't think so. They reflect the combination of two qualities: the main implementation of the language, and the community around the language. It's way too cynical to say that the results cannot be taken seriously -- they just have to be taken carefully.

For instance, we can see that the Rust community was able to make their runs quite snappy quite easily, even though the implementation isn't even ready yet. That's impressive!


They are toy examples as far as proxies for actual performance in real life. Unless real life code is a lot of n-body computations and FASTa searching, and it is run on a Debian OS with 4 core 64 bit machine (or other 3 configurations) performance need to be measured on a representative workload.

But what I was getting at is that those scores are non-toy like and important for PR purposes. And as you said, they represent how active or caring the community is (and how sensitive it is to its _perceived_ performance).

So kudos to Rust community!


Maybe D should try to get into the game again. Unfortunately, D seems not sexy enough for the maintainer.

http://forum.dlang.org/post/hfuplv$1jjl$1@digitalmars.com


Maybe you should help the D community to publish some measurements!

http://forum.dlang.org/thread/lv7h1r$mg3$2@digitalmars.com?p...


That's not the real problem with the shootout scores.

The problem is that in quest for optimization the code for many languages becomes very much non idiomatic. Therefore the result does not accurately reflect real life performance, where writing idiomatic code outweighs the benefit of iterating on something to the nth degree in search of micro optimizations.


When performance matters, code is re-written to the nth degree. When it doesn't matter, it doesn't matter.


The results are exactly what they claim to be.

What you believe they reflect is not a claim made on the website.


When you cannot get the project name right, why should we pay any attention to what you say about the benchmarks game?


> why should we pay any attention to what you say about the benchmarks game?

I very much appologize and humble myself before the almighty benchmark gods. Please forgive me.


Basic reading skills -- Fail.


I'd love to see the exclamation mark requirement removed from the rust spec. It makes macro invocation feel "wrong" or "special" in a subtle way and betrays a lack of confidence in the macro system.

I'd rather the language designers assume that future tooling for IDEs and editors will syntax highlight macros.


That's actually a feature of Rust that I liked the moment I saw it. In particular, the forced '!' suffix makes macros stand out as something that might potentially introduce non-trivial control flow; for instance, the try! macro does an early-return of the containing function on failure. In C, putting control flow in a macro is generally considered poor form; in Rust, it becomes much more sensible precisely because of the macro naming convention.


I'm not sure I agree with this. Or at least, I'm not sure I think this will hold true once macros are used for more than control flow mechanisms, which are approximately half of the uses of the macros in the stdlib right now as far as I can see. After all, most C style guidelines do insist on some way of differentiating macros, commonly making them all-caps, but it makes it no less sketchy to throw a return into a macro.


> It makes macro invocation feel "wrong" or "special" in a subtle way and betrays a lack of confidence in the macro system

But a macro invocation is special. The syntax within a macro does not follow Rust rules, so if macros would not be marked properly it would be incredibly confusing for a user. Let alone that macro imports are different by the very nature of their behavior.


I think that the 'call-by-name' behaviour in Scala is confusing, because you can write something that you think will execute once but actually, it might execute multiple times and there's no obvious marker that it's special. When you have something that is potentially major, you generally don't want it to be invisible.


Not to mention it would be difficult, if not impossible, to parse in the first place (the contents of a macro invocation are stored as token trees within macro AST nodes, within the regular AST).


Macros have a freeform syntax, with the only restriction being that delimiters within have to be balanced. I don't see how an IDE could provide general syntax highlighting for them.


The IDE could parse the macro definition and highlight accordingly. (It would know where to expect an identifier and where to expect an expression etc.)


> (It would know where to expect an identifier and where to expect an expression etc.)

But that doesn't mean that what's in the macro should be highlighted like Rust:

    sql!("SELECT * FROM users WHERE name = $1");
is a macro provided by https://github.com/sfackler/rust-postgres-macros , and should be highlighted like SQL, not like a Rust string.

    sql!("SELECT * FORM users WHERE name = $1");
throws an error, because it's malformed.


I used to think so too, especially because very basic examples with `printf!()` look "odd", but I started to like it: it gives me confidence that regular function calls won't do anything bizarre.

It's a warning that there's "magic" going on. It makes it clear that there's a lot of quirky compile-time type checking trickery behind printf, it tells that regex is going to be precompiled, etc.


Macro invocation is special. It's significantly more readable to know whether you're calling a function or invoking a macro.


It was peculiar at first with me, but I think I like it now.

Felt nearly the same way with "recur" in "clojure", instead of calling the function name recursively and pray the compiler would do the proper tail call optimization (elimination), but recur forces you to always get it right.


It makes me thing it signals mutation.


Because that's the convention in lisp?


Technically, it's not the convention in Common Lisp, but rather Scheme and Dylan. But yes.


Both Lisp and Ruby all follow that convention. Probably other languages, too, but I'm too lazy to look... :)


In Ruby, it does _not_ mean mutation. It means 'danger.' Sometimes that's mutation, sometimes its other things, like throwing an exception rather than returning nil on error.


+1. ! is the worst part of Rust macros.

There is just a terrible mismatch between the use-case macros were originally intended for (embedding different languages) and how they are mainly used now (emulating varargs).

The current arguments from Rust people are mainly defensive in that area, but maybe they will revisit this as the community matures.


  > There is just a terrible mismatch between the use-case 
  > macros were originally intended for (embedding 
  > different languages) and how they are mainly used now 
  > (emulating varargs).
This is incorrect. I can't think of any macros that are used to paper over the lack varargs (`println!` certainly doesn't count, because even if Rust had varargs you'd still need a macro here to do typesafe compile-time string parsing).

In any case, varargs can already be trivially emulated using vectors (which afaict is essentially what Go does, but Go has sugar for it). Macros would be overkill for this use case.


  > This is incorrect.
Eh ... did you have a look at the "standard" library?

  > `println!` certainly doesn't count, because even if Rust
  > had varargs you'd still need a macro here to do typesafe 
  > compile-time string parsing
Yeah, but you could do it without exposing all the horrible mess to users.

The usual "OMG!!! Look at that random `!`. Stand back, I'm using a macro here and would like to warn you that semantics will be totally different!!!"-dance is just embarrassing, especially given the fact that you can design printf-like functionality without needing varargs in the first place.

With or without macros, if your API requires warning signs it just sucks, and you should fix it instead of using "hey, but I warned people!" as an excuse.

  > varargs can already be trivially emulated using vectors
Oh right! So how do we create a vector?

    vec![1, 2, 3, 4]
Oh well ...


Great article but I wouldn't call this quick. It's quite in depth.


Readers may be interested to cf. https://en.wikipedia.org/wiki/Corecursion




Consider applying for YC's Spring batch! Applications are open till Feb 11.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: