Building an enum inside a macro - macros

Is it possible to build an enum inside a Rust macro using fields that are defined as macro parameters? I've tried this:
macro_rules! build {
($($case:ty),*) => { enum Test { $($case),* } };
}
fn main() {
build!{ Foo(i32), Bar(i32, i32) };
}
But it fails with error: expected ident, found 'Foo(i32)'
Note that if the fields are defined inside the enum, there is no problem:
macro_rules! build {
($($case:ty),*) => { enum Test { Foo(i32), Bar(i32, i32) } };
}
fn main() {
build!{ Foo(i32), Bar(i32, i32) };
}
It also works if my macro only accepts simple fields:
macro_rules! build {
($($case:ident),*) => { enum Test { $($case),* } };
}
fn main() {
build!{ Foo, Bar };
}
But I've been unable to get it to work in the general case.

It's absolutely possible, but you're conflating totally unrelated concepts.
Something like $case:ty does not mean $case is something which looks like a type, it means $case is literally a type. Enums are not made up of a sequence of types; they're made up of a sequence of variants which are an identifier followed (optionally) by a tuple structure body, a record structure body, or a tag value.
The parser doesn't care if the type you give it happens to coincidentally look like a valid variant, it's simply not expecting a type, and will refuse to parse one in that position.
What you need is to use something like $case:variant. Unfortunately for you, no such matcher exists. The only way to do something like this is to manually parse it using a recursive incremental parser and that is so out of scope of an SO question it's not funny. If you want to learn more, try the chapter on incremental TT munchers in the Little Book of Rust Macros as a starting point.
However, you don't appear to actually do anything with the cases. You're just blindly substituting them. In that case, you can just cheat and not bother with trying to match anything coherent:
macro_rules! build {
($($body:tt)*) => {
as_item! {
enum Test { $($body)* }
}
};
}
macro_rules! as_item {
($i:item) => { $i };
}
fn main() {
build!{ Foo, Bar };
}
(Incidentally, that as_item! thing is explained in the section on AST coercion (a.k.a. "the reparse trick").)
This just grabs everything provided as input to build!, and shoves it into the body of an enum without caring what it looks like.
If you were trying to do something meaningful with the variants, well, you're going to have to be more specific about what you're actually trying to accomplish, as the best advice of how to proceed varies wildly depending on the answer.

Related

Preserving struct field visibility with a macro

I'm trying to write a Rust macro that allows me to make use of the field names and types of a struct declaration, but I still need to emit the struct.
I've got it working with optional attributes, visibility of the struct (thanks to The Little Book of Rust Macros), but can't figure out how to deal with the optional presence of pub in the individual fields.
So far I've got:
macro_rules! with_generic {
($(#[$struct_meta:meta])*
pub struct $name:ident { $($fname:ident : $ftype:ty), *}
) => {
with_generic![(pub) $(#[$struct_meta])* struct $name {$($fname: $ftype) ,*}];
};
($(#[$struct_meta:meta])*
struct $name:ident { $($fname:ident : $ftype:ty), *}
) => {
with_generic![() $(#[$struct_meta])* struct $name {$($fname: $ftype), *}];
};
(
($($vis:tt)*)
$(#[$struct_meta:meta])*
struct $name:ident { $($fname:ident : $ftype:ty), *}
) => {
// emit the struct here
$(#[$struct_meta])*
$($vis)* struct $name {
$($fname: $ftype,)*
}
// I work with fname and ftypes here
}
}
And it works with something like
with_generic! {
#[derive(PartialEq, Eq, Debug)]
pub struct Person {
first_name: String,
last_name: String
}
}
or
with_generic! {
#[derive(PartialEq, Eq, Debug)]
struct PrivatePerson {
first_name: String,
last_name: String
}
}
but doesn't work with
with_generic! {
#[derive(PartialEq, Eq, Debug)]
struct MixedPerson {
pub first_name: String,
last_name: String
}
}
I'd like to get some help on how to make the macro work with that last case. I feel like I might be missing something basic here, such as the type used for binding visibility. If there's a way to bind the whole struct tree while getting the field names and types, that would also be fine.
I'd also like to learn how to get it to work with structs that have lifetime parameters, but maybe that should be a separate question.
You can't. At least, not with a single, non-recursive rule. This is because Rust doesn't have a macro matcher for visibility.
The parse-macros crate contains a parse_struct! macro that shows the work necessary to completely parse a struct definition. Short version: you need to parse each field individually, with one rule for each of "has pub" and "doesn't have pub".
I'd also just note that there's another case your macro doesn't yet account for: attributes on the fields, which is needed for doc-comments on them to work.
Quite soon, macros 1.1 should be stabilised, which might provide an easier approach (assuming you can express your macro as a derivation, and don't care about older versions of Rust).
Since Rust 1.30, you can match visibility keywords with the vis specifier. A vis metavariable will match nothing if there is no visibility keyword to match, so you don't even need to use it inside $()*. This change makes with_generic vastly simpler:
macro_rules! with_generic {
($(#[$struct_meta:meta])*
$sv:vis struct $name:ident { $($fv:vis $fname:ident : $ftype:ty), *}
) => {
// emit the struct here
$(#[$struct_meta])*
$sv struct $name {
$($fv $fname: $ftype,)*
}
// do whatever else you need here
}
}
Rust 1.15 was officially released shortly after I asked this question and brings procedural macros (custom derive) support like #DK. has said.
Going forward, I think custom derives w/ syn and quote will be the standard way of doing this kind of thing and side-steps this issue completely since you no longer need to manually re-emit the struct.

Equivalent of __func__ or __FUNCTION__ in Rust?

In C and C++ you can get the name of the currently executing function through the __func__ macro with C99 & C++11 and ___FUNCTION___ for MSVC.
Is there an equivalent of this in Rust?
Example of __func__ in C:
#include "stdio.h"
void funny_hello() {
printf ("Hello from %s\n", __func__);
}
int main() {
funny_hello();
}
Outputs Hello from funny_hello.
You can hack one together with std::any::type_name.
macro_rules! function {
() => {{
fn f() {}
fn type_name_of<T>(_: T) -> &'static str {
std::any::type_name::<T>()
}
let name = type_name_of(f);
&name[..name.len() - 3]
}}
}
Note that this gives a full pathname, so my::path::my_func instead of just my_func. A demo is available.
There was an RFC about this, but it was never agreed upon or implemented.
The rationale for its absence:
"In general I don't think any of us have given an inordinate amount of
thought to these "debugging related" macros in terms of long term
stability. Most of them seem fairly harmless, but committing to
provide all of them for all Rust programs forever is a strong
commitment to make. We may want to briefly consider the story of these
macros in conjunction with considering adding this new macro."
Maybe Rust will have something comparable in the future,
but for now you will need to rely on your own tagging.
side note: __FUNCTION__ is non standard, __func__ exists in C99 / C++11.
Adding to Veedrac's answer, you can get the function's name without its full path by adding this:
macro_rules! function {
() => {{
fn f() {}
fn type_name_of<T>(_: T) -> &'static str {
std::any::type_name::<T>()
}
let name = type_name_of(f);
// Find and cut the rest of the path
match &name[..name.len() - 3].rfind(':') {
Some(pos) => &name[pos + 1..name.len() - 3],
None => &name[..name.len() - 3],
}
}};
}
You will get my_func instead of my::path::my_func for example.
It appears that function_name crate will do this.
https://docs.rs/function_name/latest/function_name/
The example from the docs is
use ::function_name::named;
#[named]
fn my_super_duper_function ()
{
assert_eq!(
function_name!(),
"my_super_duper_function",
);
}
I am not involved with the project and have not actually tried it yet.
While there's a lack of official support, there's the stdext crate (also mentioned in the RFC issue) that makes it easy to use.
use stdext::function_name;
fn foo() {
println!("{}", function_name!());
}
This includes module/trait names, like ::module::Trait::function if any.
If you only care about the name and not its entire path, you could do something like this for trait methods (keep in mind that there is runtime overhead for this, you may want to limit it with, e.g. OnceCell):
let fn_name = function_name!()
.rsplit_once(':')
.expect("could not parse function name")
.1;

Generating documentation in macros

I have a couple of macros to reduce boilerplate when defining certain tuple-structs of the form:
macro_rules! new_type (($name:ident, $bytes:expr) => (
pub struct $name(pub [u8; $bytes]);
// some common operations on $name
));
However, I would also like to document these new structs. The best thing would be if I could write my documentation right before my macro invocation.
/// A certain type
new_type!(CertainType, 42);
However, Rust won't generate documentation for CertainType when this happens.
Another (not as flexible) alternative would be to do something like:
macro_rules! new_type (($name:ident, $bytes:expr) => (
/// Some more generic documentation for $name
pub struct $name(pub [u8; $bytes]);
// some common operations on $name
));
However, when doing that the Rust macro system doesn't expand the token $name in the documentation comment. The only alternative left is to write very generic documentation in the macro, but that would lead to my library being a lot worse documented than it could be.
What are your recommendations for handling this? The best solution for me would be to be able to write specific documentation for each macro invocation, but if that's not possible I would be grateful for hints on how to expand tokens in documentation comments.
It is possible to capture doc comments in macro invocations. It is not widely-known, but Rust documentation is actually represented as a special kind of attribute on an item. For example:
/// Some documentation comment
pub fn function() {}
// is equivalent to
#[doc="Some documentation comment"]
pub fn function() {}
And it is possible to capture attributes in macros. There are already several macros which use this ability, the most used probably being bitflags!:
macro_rules! bitflags {
(
$(#[$outer:meta])*
pub struct $BitFlags:ident: $T:ty {
$(
$(#[$inner:ident $($args:tt)*])*
const $Flag:ident = $value:expr;
)+
}
) => { /* ... */ };
// ...
}
Note the $(#[$outer:meta])* and $(#[$inner:meta])* parts of the pattern. These capture all attributes placed before the respective item in the pattern. If you write a doc comment there, it will be converted to the doc attribute and will be passed to rustdoc, as usual.
The following is an example from the quick_error crate which also uses this approach:
quick_error! {
#[derive(Debug)]
pub enum SomeError {
/// IO Error
Io(err: io::Error) {}
/// Arbitrary system error
Sys(errno: nix::Errno) {}
}
}
It does work — here is an example of the structure generated by quick_error macro, and here is its definition.

How do I write a wrapper for a macro without repeating the rules?

I am trying to make a wrapper for a macro. The trouble is that I don't want to repeat the same rules in both macro. Is there a way to do that?
Here is what I tried:
macro_rules! inner {
($test:ident) => { stringify!($test) };
($test:ident.run()) => { format!("{}.run()", stringify!($test)) };
}
macro_rules! outer {
($expression:expr) => {
println!("{}", inner!($expression));
}
}
fn main() {
println!("{}", inner!(test));
println!("{}", inner!(test.run()));
outer!(test);
outer!(test.run());
}
but I get the following error:
src/main.rs:8:31: 8:42 error: expected ident, found test
src/main.rs:8 println!("{}", inner!($expression));
^~~~~~~~~~~
If I change the outer macro for this, the code compile:
macro_rules! outer {
($expression:expr) => {
println!("{}", stringify!($expression));
}
}
What am I doing wrong?
macro_rules! is both cleverer and dumber than you might realise.
Initially, all input to a macro begins life as undifferentiated token soup. An Ident here, StrLit there, etc. However, when you match and capture a bit of the input, generally the input will be parsed in an Abstract Syntax Tree node; this is the case with expr.
The "clever" bit is that when you substitute this capture (for example, $expression), you don't just substitute the tokens that were originally matched: you substitute the entire AST node as a single token. So there's now this weird not-really-a-token in the output that's an entire syntax element.
The "dumb" bit is that this process is basically irreversible and mostly totally invisible. So let's take your example:
outer!(test);
We run this through one level of expansion, and it becomes this:
println!("{}", inner!(test));
Except, that's not what it looks like. To make things clearer, I'm going to invent some non-standard syntax:
println!("{}", inner!( $(test):expr ));
Pretend that $(test):expr is a single token: it's an expression which can be represented by the token sequence test. It is not simply the token sequence test. This is important, because when the macro interpreter goes to expand that inner! macro, it checks the first rule:
($test:ident) => { stringify!($test) };
The problem is that $(test):expr is an expression, not an identifier. Yes, it contains an identifier, but the macro interpreter doesn't look that deep. It sees an expression and just gives up.
It fails to match the second rule for the same reason.
So what do you do? ... Well, that depends. If outer! doesn't do any sort of processing on its input, you can use a tt matcher instead:
macro_rules! outer {
($($tts:tt)*) => {
println!("{}", inner!($($tts)*));
}
}
tt will match any token tree (see the Macros chapter of the Rust Book). $($tts:tt)* will match any sequence of tokens, without changing them. This of this as a way to safely forward a bunch of tokens to another macro.
If you need to do processing on the input and forward it on to the inner! macro... you're probably going to have to repeat the rules.
I had some success with the $($stuff: expr),+ syntax.
macro_rules! println {
( $($stuff: expr),+) => {
avr_device::interrupt::free(|cs| {
uwriteln!(unsafe { &SERIAL_STATIC}.borrow(cs).borrow_mut().as_mut().unwrap(),
$($stuff),+)
})
}
}

Shorthand for referring to Perl/Moose package names?

In both Python and Java we have import to eliminate the repetition of fully-qualified package/module names throughout code. Is there any equivalent in Perl/Moose? I think it would really make Moose nicer to use if we didn't have to repeat MyApp::Model::Item. Instead, I'd like to [somehow declare] MyApp::Model::Item; and later on, simply refer to Item. I can think of all of these use-cases where class names are used...
extends 'Item';
with 'ItemRole';
Item->new(name => 'thing');
method foo(Item $xyz) { ... }, with MooseX::Method::Signatures
$var->isa('Item');
try { ... } catch (DatabaseError $e) { ... }, with TryCatch
$Item::SOME_PACKAGE_GLOBAL_VARIABLE
If there is no such thing yet, any idea on how I might start to cleanly implement this? I can see that it would be tricky to deal with cases where the classname is used as a string.
This all works with aliased
use aliased 'MyApp::Model::Item';
use aliased 'MyApp::ItemRole';
use aliased 'MyApp::Exception::DatabaseError';
extends Item;
with ItemRole;
Item->new(name => 'thing');
method foo (Item $xyz) { ... }
$var->isa(Item);
try { ... } catch(DatabaseError $e) { ... }
This doesn't:
$Item::SOME_PACKAGE_GLOBAL_VAR
Needing something like that seems to be quite rare, but I suppose it could be made to work with the namespace::alias module.