Use output of macro as parameter for another macro - macros

I am trying to implement a generic Point<T> type for small dimensions.
To achieve that, I wrote a macro that takes the name of the new type and the dimension of the point (since, as far as I know, Rust doesn't allow for numerical generics).
macro_rules! define_point {
($type_name: ident, $dimension: expr) => {
pub struct $type_name<T> {
coords: [T; $dimension]
}
}
}
I use it like this:
define_point!(Point2, 2);
define_point!(Point3, 3);
This works fine. I also implement the Index trait on my Point type in this macro to access the coordinates directly.
Now I want some convenience functions for accessing the coordinates of my point as follows: p.x(), p.y() or p.z() depending on the dimension.
To do that, I have another macro:
macro_rules! impl_point_accessors {
($type_name: ident, $coord_name: ident, $coord_index: expr) => {
impl<T> $type_name<T> {
pub fn $coord_name(&self) -> T {
&self[$coord_index]
}
}
};
($type_name: ident, $coord_name: ident, $coord_index: expr, $($extra_coord_name: ident, $extra_coord_index: expr),+) => {
impl_point_accessors!($type_name, $coord_name, $coord_index);
impl_point_accessors!($type_name, $($extra_coord_name, $extra_coord_index), +);
}
}
I use it as follows:
impl_point_accessors!(Point2, x, 0, y, 1);
impl_point_accessors!(Point3, x, 0, y, 1, z, 2);
This seems to work when I look at the result of rustc --pretty=expanded.
Now, as an exercise, I wrote this other macro that would give me the list x, 0, y, 1, ... from the dimension directly:
macro_rules! dimension_to_coord_pairs {
(1) => {
x, 0
};
(2) => {
x, 0, y, 1
};
(3) => {
x, 0, y, 1, z, 2
};
(4) => {
x, 0, y, 1, z, 2, w, 3
};
}
However, when I try to use the output of this new macro like this:
impl_point_accessors!($type_name, dimension_to_coord_pairs!($dimension));
It looks like the dimension_to_coord_pairs macro does not get expanded into the list of arguments that I want.
My question now: Is there any way to tell Rust to expand the macro and use the expanded syntax as my list of arguments in another macro ?

A macro can invoke another macro, but one cannot take the result of another. Each macro invocation must result in legal code, which can include other macro invocations, but the compiler should never have to figure out which macro you intended to be invoked first.
You can work around your problem by reorganising the macros to be completely top-down, something like this:
macro_rules! define_point {
($type_name: ident, $dimension: tt) => {
pub struct $type_name<T> {
coords: [T; $dimension]
}
impl_point_accessors!($type_name, $dimension);
}
}
macro_rules! impl_point_accessors {
($type_name: ident, $dimension: tt) => {
impl<T> $type_name<T> {
write_coord_getters!($dimension);
}
};
}
macro_rules! coord_getter {
($coord_name: ident, $coord_index: expr, $ret: ty) => {
pub fn $coord_name(&self) -> &T {
&self.coords[$coord_index]
}
}
}
macro_rules! write_coord_getters {
(1) => {
coord_getter!(x, 1, T);
};
(2) => {
write_coord_getters!(1);
coord_getter!(y, 2, T);
};
(3) => {
write_coord_getters!(2);
coord_getter!(z, 3, T);
};
(4) => {
write_coord_getters!(3);
coord_getter!(w, 4, T);
};
}
It's not quite as tidy as you were attempting, but it still lets you invoke it the way you wanted:
define_point!(Point3, 3);
Notice that I changed $dimension: expr to $dimension: tt. I'm not 100% sure why this is the case but, inside a macro, a variable of type expr cannot match a literal.
Also, I changed the return type to &T instead of T. You could also fix the same problem by making T: Copy instead.

My question now: Is there any way to tell Rust to expand the macro and use the expanded syntax as my list of arguments in another macro ?
No. Macros are syntactic, not lexical. That is, a macro cannot expand to an arbitrary bundle of tokens. Even if it could, you would need some way to force the compiler to expand the inner macro before the outer one, and you can't do that either.
The closest you can get is to use a "callback" style:
macro_rules! impl_point_accessors {
($type_name: ident, $coord_name: ident, $coord_index: expr) => {
impl<T> $type_name<T> {
pub fn $coord_name(&self) -> T {
panic!("coord {}", $coord_index);
}
}
};
($type_name: ident, $coord_name: ident, $coord_index: expr, $($extra_coord_name: ident, $extra_coord_index: expr),+) => {
impl_point_accessors!($type_name, $coord_name, $coord_index);
impl_point_accessors!($type_name, $($extra_coord_name, $extra_coord_index), +);
}
}
macro_rules! dimension_to_coord_pairs {
(1, then $cb:ident!($($cb_args:tt)*)) => {
$cb!($($cb_args)* x, 0);
};
(2, then $cb:ident!($($cb_args:tt)*)) => {
$cb!($($cb_args)* x, 0, y, 1);
};
(3, then $cb:ident!($($cb_args:tt)*)) => {
$cb!($($cb_args)* x, 0, y, 1, z, 2);
};
(4, then $cb:ident!($($cb_args:tt)*)) => {
$cb!($($cb_args)* x, 0, y, 1, z, 2, w, 3);
};
}
struct Point<T>(Vec<T>);
dimension_to_coord_pairs!(2, then impl_point_accessors!(Point,));

Related

Calling functions with different numbers of arguments in Rust macros

I need a macro that will call functions with different numbers of arguments or a macro that will generate a valid argument list from its (repeating) parameters.
I am fine with explicitly giving the information about the number of arguments to the macro, but I can't figure out how to generate the argument list for the function - I always stumble on the macros returning expressions rather than token tree.
I made the following playground example:
macro_rules! call (
($f: expr, $($params:tt)*) => {
$f(make_params!($($params:tt)*))
};
);
macro_rules! make_params {
() => {};
(I $($params: tt)*) => {
1, make_params!($($params:tt)*)
};
}
fn foo(a: i32, b: i32, c: i32) {
println!("foo: {} {} {}", a, b, c);
}
fn bar(a: i32, b: i32) {
println!("bar: {} {}", a, b);
}
fn main() {
call!(foo, I I I);
call!(bar, I I);
}
The compiler complains with the following:
error: macro expansion ignores token `,` and any following
--> src/main.rs:10:10
|
10 | 1, make_params!($($params:tt)*)
| ^
|
note: caused by the macro expansion here; the usage of `make_params!` is likely invalid in expression context
--> src/main.rs:3:12
|
3 | $f(make_params!($($params:tt)*))
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
...
How can I treat the return of make_params! as a token stream (or such) rather than expression?
My real use case is a bit more involved than this toy example. My functions have multiple parameter types which are constructed in different ways. In my case, just making macros call1, call2!, ... does not seem like a good solution, as I would need the likes of call_IIOOI, call_IIIO, etc.
You need to build the function call progressively as you go and only emit it at once in the end:
macro_rules! call (
($f: expr, $($params:tt)*) => {
make_call!($f, () $($params)*)
};
);
macro_rules! make_call {
($f: expr, ($($args:tt)*)) => { $f($($args)*) };
($f: expr, () I $($params:tt)*) => {
make_call!($f, (1) $($params)*)
};
($f: expr, ($($args:tt)*) I $($params:tt)*) => {
make_call!($f, ($($args)*, 1) $($params)*)
};
}
playground

Why underline not work in some scenario of monad?

Underline _ cannot work for some scenario in following code:
case class Item
(
para: Int
)
val a = List(1, 2)
val b = List(Item(1), Item(2))
a foreach {
perA => println(perA * 2) // Line A: ok
}
a foreach {
println(_) // Line B: ok
}
a foreach {
println(_ * 2) // Line C: not ok
}
b foreach {
perB => println(perB.para) // line D: ok
}
b foreach {
println(_.para) // line E: not ok
}
Could someone explain for me about line C & line E, thanks.
TL;DR: the argument of the lambda can be inserted at an unexpected position. Instead of taking
a foreach { println(_.blah) }
and building
a foreach { x => println(x.blah) }
out of it, the compiler instead builds
a foreach { println( x => x.blah ) }
and then fails to derive the type for the argument x.
A) This is the most explicit way to write down the lambda, the only way to make it clearer would be to add a type:
a foreach { perA => println(perA * 2) }
is the same as
a foreach { (x: Int) => println(x * 2) }
This should obviously work.
B) This works because it's one way to write down the function automatically generated from println. It is equivalent to any of the six variants below:
a foreach { (x: Int) => println(x) }
a foreach { x => println(x) }
a foreach { println(_) }
a foreach { println _ }
a foreach { println }
a foreach println
C) Because of the * 2, this here
a foreach { println(_ * 2) }
can no longer be considered just println(_). Instead, it is interpreted as
a foreach { println( { (x: ??!) => x * 2 } ) }
and since println takes no function-valued arguments, it cannot determine what the type of x is supposed to be, and exits with an error.
D) Is essentially the same as A, it works, I hope it's clear.
E) Is a variation of C, but this time, the type-checker isn't looking for something with method *(i: Int), but instead it's looking for something with a member para.
This here:
b foreach { println(_.para) }
is again interpreted as a foreach with a function which ignores elements of b and returns the constant value of the expression println(_.para),
that is:
b foreach { println( { (x: ??!) => x.para } ) }
Again, the inner expression println( { (x: ??!) => x.para } ) does not make any sense, because println does not expect function-valued arguments (it can handle Any, but it's not enough to derive the type of x).

Match an underscore instead of ident in macro

I'm creating a macro that matches two expressions and an identifier. I would like to be able to ignore the identifier if it's not needed, but the compiler seems to complain if I use _ there.
My macro:
macro_rules! if_some {
($x:expr, $id:ident, $expr:expr) => {
match $x {
None => None,
Some($id) => Some($expr),
}
};
}
What I'd like to do:
if_some!(obtain_an_option(), x, do_something_with(x))
and
if_some!(obtain_an_option(), _, do_something())
The second call fails.
I worked around it by defining a second macro if_some_! that doesn't receive an identifier (I could not use a second pattern either). I'm sure there's a way to say "here accept an identifier or just _.
Maybe there's already a macro/function for this (like Option::map now I think about it)... nevertheless it'd be nice to now.
The simplest way is to add a second arm that matches an underscore:
macro_rules! if_some {
($x:expr, _, $expr:expr) => {
match $x {
None => None,
Some(_) => Some($expr),
}
};
($x:expr, $id:ident, $expr:expr) => {
match $x {
None => None,
Some($id) => Some($expr),
}
};
}
And, yes, this sounds like you just want Option::map.
Option::map seems to be the best solution for this particular problem, but when you really need a macro which expect both idents and _ as a pattern, you can also use the $p:pat fragment. The fragment of course accepts a broader range of patterns like (ref x, y), but typically this will be acceptable.
macro_rules! if_some {
($x:expr, $p:pat, $expr:expr) => {
match $x {
None => None,
Some($p) => Some($expr),
}
};
}
fn main() {
println!("{:?}", if_some!(Some(12), x, x + 1)); // Some(13)
println!("{:?}", if_some!(Some(12), _, 1)); // Some(1)
}

Can macros match against constant arguments instead of literals?

Given the macro matching example, this shows how macros can match an argument.
I've made very minor changes here to use numbers:
macro_rules! foo {
(0 => $e:expr) => (println!("mode X: {}", $e));
(1 => $e:expr) => (println!("mode Y: {}", $e));
}
fn main() {
foo!(1 => 3);
}
Works, printing: mode Y: 3
However I would like to use a constant as an argument, can this be made to work:
const CONST: usize = 1;
macro_rules! foo {
(0 => $e:expr) => (println!("mode X: {}", $e));
(1 => $e:expr) => (println!("mode Y: {}", $e));
}
fn main() {
foo!(CONST => 3);
}
Is this possible in Rust?
Note, using a regular match statement isn't usable for me, since in my code each branch resolves to different types, giving an error.
So I'm specifically interested to know if a constant can be passed to a macro.
No.
Macros operate on the Abstract Syntax Tree, so they reason at the syntactic level: they reason about tokens and their spelling.
For example:
fn main() {
let v = 3;
}
In this case, the AST will look something like:
fn main
\_ let-binding v
\_ literal 3
If you ask a macro whether v is 3, it will look at you funny, and wonder why you would try comparing a variable name and a literal.
I'm fairly sure the answer is "no"; at macro expansion time all you have are token trees - expansion happens before evaluation, or even type inference/checking.
const CONST: usize = 0;
macro_rules! foo {
($i:ident => $e:expr) => {
if $i == 0 {
println!("mode X: {}", $e);
} else if $i == 1 {
println!("mode Y: {}", $e);
}
};
}
fn main() {
foo!(CONST => 3);
}
If you want use identifier in macro it needs to be ident tag and you can use if, else if blocks instead of match.

How to write a macro in Rust to match any element in a set?

In C, I'm used to having:
if (ELEM(value, a, b, c)) { ... }
which is a macro with a variable number of arguments to avoid typing out
if (value == a || value == b || value == c) { ... }
A C example can be seen in Varargs `ELEM` macro for use with C.
Is this possible in Rust? I assume it would use match. If so, how would variadic arguments be used to achieve this?
macro_rules! cmp {
// Hack for Rust v1.11 and prior.
(#as_expr $e:expr) => { $e };
($lhs:expr, $cmp:tt any $($rhss:expr),*) => {
// We do this to bind `$lhs` to a name so we don't evaluate it multiple
// times. Use a leading underscore to avoid an unused variable warning
// in the degenerate case of no `rhs`s.
match $lhs { _lhs => {
false || $(
cmp!(#as_expr _lhs $cmp $rhss)
) || *
// ^- this is used as a *separator* between terms
}}
};
// Same, but for "all".
($lhs:expr, $cmp:tt all $($rhss:expr),*) => {
match $lhs { _lhs => {
true && $( cmp!(#as_expr _lhs $cmp $rhss) ) && *
}}
};
}
fn main() {
let value = 2;
if cmp!(value, == any 1, 2, 3) {
println!("true! value: {:?}", value);
}
if cmp!(value*2, != all 5, 7, 1<<7 - 1) {
println!("true! value: {:?}", value);
}
}
First off, if your a, b, and c are concrete values, you can just use match:
fn main() {
let x = 42;
match x {
1 | 2 | 3 => println!("foo"),
42 => println!("bar"),
_ => println!("nope"),
}
}
If you want to match on variables you need to write the match arms like this:
match x {
x if x == a || x == b || x == c => println!("foo"),
42 => println!("bar"),
_ => println!("nope"),
}
…which is basically what you want to avoid.
But: A pretty direct translation of your C macro is also possible!
macro_rules! elem {
($val:expr, $($var:expr),*) => {
$($val == $var)||*
}
}
fn main() {
let y = 42;
let x = 42;
if elem!(x, 1, 3, y) {
println!("{}", x);
}
}
I'm partial to writing this without a macro, taking advantage of contains on arrays.
fn main() {
if [1, 2, 3, 4].contains(&4) {
println!("OK");
}
}
It's hard to predict what will happen to this when optimized, but if absolute performance is a goal you'd do well to benchmark each approach.
Yes this is possible, the following macro expands to do each check.
macro_rules! elem {
($n:expr, $( $hs:expr ),*) => ($( $n == $hs )||* );
}
fn main() {
if elem!(4, 1, 2, 3, 4) {
println!("OK");
}
}
Thanks to #vfs on #rust in IRC.