Inspired by @Yoric's interesting experiments I set out to see how un-obtrusive and zero-cost I could make a procedural macro implementation. The best I got was a single attribute, and having to throw a !
in at each call-site.
I put together the named crate. Sample code:
use named::named;
#[named]
fn or(a: bool, b: bool) -> bool {
a || b
}
fn main() {
or!(a = true, b = false);
}
It also supports optional default values:
use named::named;
#[named(defaults(b = false))]
fn or(a: bool, b: bool) -> bool {
a || b
}
fn main() {
or!(a = true);
or!(a = true, b = true);
}
The error messages are decent, too; see some trybuild test cases for examples.
I think this shows that such a feature could reasonably live outside the language. Interesting limitations we would want to overcome if we wanted to turn this into something more real:
- No support for functions defined in
impl
blocks. Attribute macros aren't allowed onfn
s insideimpl
blocks. This could probably be worked around by having the attribute macro on the containing item, and a helper attribute on the function, but really, it would be nicer to not need to jump through those hoops. I'm not sure why this restriction is in place, I guess to avoid needing to provide surrounding context to the macro? Adding this support could probably help with other use-cases, too. - No support for functions which take receivers (i.e.
self
). Postfix macros could potentially help here. - Macros can't be defined as members of
Struct
s - you can't define a macro such thatFoo::macro!()
can be called. This feels reasonably easily fixable if we wanted to. - Error messages are a bit limited in terms of context collection. To avoid a combinatorial explosion of match conditions on the macro, error messages if you give arguments in the wrong order, or give multiple unknown arguments, only mention the first problem they encounter. This is the only thing that pushes me towards thinking if we wanted to make this something real it should be done in the language, where more complex matching logic can be written. That said, there are probably more clever things this macro could do, or features we could introduce to the macro system to support this better, and also the error messages aren't terrible, just not as rich as they could be.
But in general, I dislike this interface less than I was expecting to...! I'd be interested to know what other people think.