How to pass a macro containing multiple items into a macro? - macros

Given this simple macro that expands multiple items, how could this take a macro as an argument?
macro_rules! print_structs {
($($t:ty)*) => ($(
println!("{:?}", TypeId::of::<$t>());
)*)
}
// expands one println per type!
print_structs! { i8 i16 usize String }
How could a pre-defined macro of types be passed in?
Example of non-working macro:
macro_rules! some_types {
() => {
i8 i16 usize String
}
}
print_structs! { some_types!() }
See play.rust-lang.org example, uncomment UNCOMMENT TO TEST lines to see the problem.
Gives the error: macro expansion ignores token `i16` and any following
I also tried to put the list in a file to include, eg:
print_structs! {
include!("some_types.in")
}
... however this gives an error: expected type, found `include!("../struct_list.rs")`

From looking into this, it seems that its not possible to expand a list inside a macro using a macro or include.
Although code-generation is an option, its quite involved so will leave it out of this answer.
It is possible to get similar functionality by swapping macro use around, instead of passing the list into a macro, pass a macro name into a generic macro that expand it with a list.
Heres a working example:
macro_rules! print_structs {
($($t:ty)*) => ($(
println!("{:?}", ::std::any::TypeId::of::<$t>());
)*)
}
macro_rules! apply_macro_to_structs {
($macro_id:ident) => {
$macro_id! {
i8 i16 usize String
}
}
}
fn test_a() {
// expands one println per type!
print_structs! { i8 i16 usize String }
}
fn test_b() {
// expand using a macro
apply_macro_to_structs!(print_structs);
}
fn main() {
test_a();
test_b();
}

Related

Why can I not match a struct's body using the `block` matcher in a Rust macro?

I am trying to match a struct in a Rust macro. I need to pull the
struct apart somewhat to get its name. I figured that the block
matcher would do the trick. Consider, for example this:
macro_rules! multi {
(struct $name:ident $block:block) => {
enum George {$name}
}
}
multi!{
struct Fred {
a:String
}
}
which expands to
enum George { Fred, }
which is about right.
However, if I turn this back into a struct, it fails.
macro_rules! multi {
(struct $name:ident $block:block) => {
struct $name $block
}
}
which gives this error.
error: expected `where`, `{`, `(`, or `;` after struct name, found `{ a: String }`
--> src/main.rs:64:22
|
64 | struct $name $block
| ^^^^^^ expected `where`, `{`, `(`, or `;` after struct name
It looks like {a: String} is being treated as a single token, rather
than being re-parsed; but it is what should be going in there.
The $:block matcher is for block expressions, i.e. a set of curly braces containing zero or more Rust statements and items and an optional trailing return value. For example the following are blocks:
{
let x = 1;
}
{
#[derive(Default)]
struct S;
S::default()
}
Examples of curly braces in Rust that are blocks are:
function bodies,
if and else clauses,
loop bodies of for, while, and loop loops.
The curly braces around the fields of a struct are not a block because they are not supposed to contain zero or more Rust statements and items followed by an optional trailing return value. Instead they are supposed to contain the names and types of the struct fields.
In a macro you can match an arbitrary set of curly braces using the pattern { $($tt:tt)* }, which means "curly braces containing any number of arbitrary tokens."
macro_rules! multi {
(struct $name:ident { $($tt:tt)* }) => {
struct $name { $($tt)* }
};
}
multi! {
struct Fred {
a: String,
}
}

How to force Haxe macro return type to Array / Iterable?

I want to write a macro that returns an (expression of an) Array -- but I can't seem to convince the compiler that my returned value will be typed as an Array. I always get "you can't iterate on a Dynamic value", even though I've tried:
Explicitly typing the return as: ExprOf<Array<Whatever>>
Inserting a type hint in the output
http://try-haxe.mrcdk.com/#D7D82
import haxe.macro.Context;
import haxe.macro.Expr;
class Test {
static function main() {
trace("Haxe is great!");
// ERROR: You can't iterate on a Dynamic value
for (val in Macro.someArrayExpr()) {
trace(val);
}
}
}
class Macro
{
public static macro function someArrayExpr():ExprOf<Array<String>>
{
// Neither of these works:
// Try to insert a type hint:
// return Context.parse('([]:Array<String>)', Context.currentPos());
return macro [];
}
}
Uh oh, it looks like it's a side effect of defining my Macro class in the same module (file) as my invocation. Separating the classes into separate files makes it work!
http://try-haxe.mrcdk.com/#57801
Test.hx:
class Test {
static function main() {
trace("Haxe is great!");
// Hooray, it works!
for (val in Macro.someArrayExpr()) {
trace(val);
}
}
}
Macro.hx:
import haxe.macro.Context;
import haxe.macro.Expr;
//use this for macros or other classes
class Macro
{
public static macro function someArrayExpr():ExprOf<Array<String>>
{
return macro ["a", "b", "c"];
}
}
The technical explanation for this (thanks to Juraj): the Test class is being typed in the macro context. In that case, it's calling the macro from a macro, which is always typed Dynamic, hence the error. So another solution is to exclude the class Test from being compiled into the macro context: http://try-haxe.mrcdk.com/#1f3b2

Is it possible to write a macro to generate N enum variants based on a common name? [duplicate]

I'd like to create a setter/getter pair of functions where the names are automatically generated based on a shared component, but I couldn't find any example of macro rules generating a new name.
Is there a way to generate code like fn get_$iden() and SomeEnum::XX_GET_$enum_iden?
If you are using Rust >= 1.31.0 I would recommend using my paste crate which provides a stable way to create concatenated identifiers in a macro.
macro_rules! make_a_struct_and_getters {
($name:ident { $($field:ident),* }) => {
// Define the struct. This expands to:
//
// pub struct S {
// a: String,
// b: String,
// c: String,
// }
pub struct $name {
$(
$field: String,
)*
}
paste::item! {
// An impl block with getters. Stuff in [<...>] is concatenated
// together as one identifier. This expands to:
//
// impl S {
// pub fn get_a(&self) -> &str { &self.a }
// pub fn get_b(&self) -> &str { &self.b }
// pub fn get_c(&self) -> &str { &self.c }
// }
impl $name {
$(
pub fn [<get_ $field>](&self) -> &str {
&self.$field
}
)*
}
}
};
}
make_a_struct_and_getters!(S { a, b, c });
My mashup crate provides a stable way to create new identifiers that works with any Rust version >= 1.15.0.
#[macro_use]
extern crate mashup;
macro_rules! make_a_struct_and_getters {
($name:ident { $($field:ident),* }) => {
// Define the struct. This expands to:
//
// pub struct S {
// a: String,
// b: String,
// c: String,
// }
pub struct $name {
$(
$field: String,
)*
}
// Use mashup to define a substitution macro `m!` that replaces every
// occurrence of the tokens `"get" $field` in its input with the
// concatenated identifier `get_ $field`.
mashup! {
$(
m["get" $field] = get_ $field;
)*
}
// Invoke the substitution macro to build an impl block with getters.
// This expands to:
//
// impl S {
// pub fn get_a(&self) -> &str { &self.a }
// pub fn get_b(&self) -> &str { &self.b }
// pub fn get_c(&self) -> &str { &self.c }
// }
m! {
impl $name {
$(
pub fn "get" $field(&self) -> &str {
&self.$field
}
)*
}
}
}
}
make_a_struct_and_getters!(S { a, b, c });
No, not as of Rust 1.22.
If you can use nightly builds...
Yes: concat_idents!(get_, $iden) and such will allow you to create a new identifier.
But no: the parser doesn't allow macro calls everywhere, so many of the places you might have sought to do this won't work. In such cases, you are sadly on your own. fn concat_idents!(get_, $iden)(…) { … }, for example, won't work.
There's a little known crate gensym that can generate unique UUID names and pass them as the first argument to a macro, followed by a comma:
macro_rules! gen_fn {
($a:ty, $b:ty) => {
gensym::gensym!{ _gen_fn!{ $a, $b } }
};
}
macro_rules! _gen_fn {
($gensym:ident, $a:ty, $b:ty) => {
fn $gensym(a: $a, b: $b) {
unimplemented!()
}
};
}
mod test {
gen_fn!{ u64, u64 }
gen_fn!{ u64, u64 }
}
If all you need is a unique name, and you don't care what it is, that can be useful. I used it to solve a problem where each invocation of a macro needed to create a unique static to hold a singleton struct. I couldn't use paste, since I didn't have unique identifiers I could paste together in the first place.

Is it possible to nest a struct declaration in a macro to apply struct attributes?

If we want to generalize applying #[derive(...)] to a struct, in some cases it would be useful to wrap this in a macro.
non-working example:
my_traits!(
pub struct MyType(u32),
MyType
);
Where my_traits could prefix the first argument with #[derive(...)], and use the second argument to declare impl SomeTrait for $t {...}.
Declaring implementations works without any problems, however I didn't manage to find a way to use a macro to prefix the struct declaration with attributes.
See this question for an example of what this could be used for:Possible to derive attributes *after* a struct declaration?
Putting #[derive(...)] into the macro seems to work fine:
#[derive(Eq,PartialEq,Debug)]
struct Foo(u32);
macro_rules! my_eq(
($name:ident) => {
#[derive(Eq,PartialEq,Debug)]
struct $name(u32);
};
);
my_eq!(Bar);
fn main() {
assert_eq!(Foo(3), Foo(3));
assert!(Foo(3) != Foo(4));
assert_eq!(Bar(3), Bar(3));
assert!(Bar(3) != Bar(4));
}
Playground link
Or if you want to pass the whole struct in:
macro_rules! my_eq(
($name:item) => {
#[derive(Eq,PartialEq,Debug)]
$name
};
);
my_eq!(struct Bar(u32););
Playground
Note that the macro takes an entire item, so the semicolon inside the macro call is needed (Foo{} structs don't need it, just like when written inline).
This can be done by passing in an item, answer thanks to #j_ey on IRC.
macro_rules! ideas {
($ty: item, $id: ident) => {
#[derive(Debug)]
$ty
impl $id {
fn doit(){}
}
}
}
ideas!(pub struct Foo(u32);, Foo);
fn main() {
let f = Foo(1);
println!("{:?}", f);
}

How to enforce that a type implements a trait at compile time?

I want to write a macro like this:
macro_rules! a {
( $n:ident, $t:ty ) => {
struct $n {
x: $t
}
}
}
But $t should implement Add, Sub and Mul traits. How can I check it at compile-time?
First, solve the problem without macros. One solution is to create undocumented private functions that will fail compilation if your conditions aren't met:
struct MyType {
age: i32,
name: String,
}
const _: () = {
fn assert_send<T: Send>() {}
fn assert_sync<T: Sync>() {}
// RFC 2056
fn assert_all() {
assert_send::<MyType>();
assert_sync::<MyType>();
}
};
Then, modify the simple solution to use macros:
macro_rules! example {
($name:ident, $field:ty) => {
struct $name {
x: $field,
}
const _: () = {
fn assert_add<T: std::ops::Add<$field, Output = $field>>() {}
fn assert_mul<T: std::ops::Mul<$field, Output = $field>>() {}
// RFC 2056
fn assert_all() {
assert_add::<$field>();
assert_mul::<$field>();
}
};
};
}
example!(Moo, u8);
example!(Woof, bool);
In both cases, we create a dummy const value to scope the functions and their calls, avoiding name clashes.
I would then trust in the optimizer to remove the code at compile time, so I wouldn't expect any additional bloat.
Major thanks to Chris Morgan for providing a better version of this that supports non-object-safe traits.
It's worth highlighting RFC 2056 which will allow for "trivial" constraints in where clauses. Once implemented, clauses like this would be accepted:
impl Foo for Bar
where
i32: Iterator,
{}
This exact behavior has changed multiple times during Rust's history and RFC 2056 pins it down. To keep the behavior we want in this case, we need to call the assertion functions from another function which has no constraints (and thus must always be true).