How to get index of macro repetition single element - macros

I need to get index of macro repetition element to write next code:
struct A {
data: [i32; 3]
}
macro_rules! tst {
( $( $n:ident ),* ) => {
impl A {
$(
fn $n(self) -> i32 {
self.data[?] // here I need the index
}
),*
}
}
}
I know one way to do it: just tell user to write index by hands:
( $( $i:ident => $n:ident ),* )
But is there a more elegant way which does not require user's action?

The easiest way is to use recursion, like so:
struct A {
data: [i32; 3]
}
macro_rules! tst {
(#step $_idx:expr,) => {};
(#step $idx:expr, $head:ident, $($tail:ident,)*) => {
impl A {
fn $head(&self) -> i32 {
self.data[$idx]
}
}
tst!(#step $idx + 1usize, $($tail,)*);
};
($($n:ident),*) => {
tst!(#step 0usize, $($n,)*);
}
}
tst!(one, two, three);
fn main() {
let a = A { data: [10, 20, 30] };
println!("{:?}", (a.one(), a.two(), a.three()));
}
Note that I changed the method to take &self instead of self, since it made writing the example in the main function easier. :)
Each step in the recursion just adds 1 to the index. It is a good idea to use "typed" integer literals to avoid compilation slowdown due to lots and lots of integer inference.

Related

Why is a negative integer not a valid pattern in a macro?

Let us consider a simple enum implementation with a static method that check whether a value has an associated value (the efficiency of the implementation is not to be regarded here):
enum Letter {
Alpha = -1,
A = 0,
B = 1,
C = 2,
}
impl Letter {
pub fn in_enum(value: isize) -> bool
{
match value {
-1 => true,
0 => true,
1 => true,
2 => true,
_ => false,
}
}
}
Now, let us write a macro for building enums with an equivalent in_enum method. The macro below was written with some guidance from the Serde guide for enum deserialization as numbers, in which matching for enum variant values also occurs.
macro_rules! my_enum {
($name:ident { $($variant:ident = $value:expr, )* }) => {
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum $name {
$($variant = $value,)*
}
impl $name {
pub fn in_enum(value: isize) -> bool
{
match value {
$( $value => true, )*
_ => false,
}
}
}
}
}
my_enum!(Letter {
Alpha = -1,
A = 0,
B = 1,
C = 2,
});
Playground.
With version 1.18.0, the compiler won't accept the variant with a negative integer.
error: expected pattern, found `-1`
--> src/main.rs:13:24
|
13 | $( $value => true, )*
| ^^^^^^
This seems to happen regardless of how I write this pattern down in the macro, or whether I use i32 or isize for the value method parameter. Changing the fragment specifier of $value to pat is also out of the question: the compiler will refuse to build the enum, even without negative variant values.
error: expected expression, found `-1`
--> src/main.rs:5:26
|
5 | $($variant = $value,)*
| ^^^^^^
What's surprising about this is that it works without using macros, as well as when I discard the Alpha variant.
Why does this happen?
This is a bug in the compiler and is already fixed in the nightly version as of today (Jul 5, 2017), and released in stable version 1.20.0.

Is it possible to write a macro that counts items and generates an enum?

I want something like this:
define_enum_and_all_variants! ( Test {
One, Two, Three
});
to produce:
enum Test {
One, Two, Three
}
const ALL_VARIANTS: [Test; 3] = [One, Two, Three];
the problem here is 3, I can write something like this:
macro_rules! define_enum_and_all_variants {
($Name:ident { $($Variant:ident),* }) =>
{
#[derive(Debug)]
enum $Name {
$($Variant),*,
}
#[allow(dead_code)]
const ALL_VARIANTS: [$Name; 3] = [$($Name::$Variant),*];
}
}
but how to calculate number of elements in enum?
This doesn't really answer the question, but only because you have a xy problem.
Instead of writing
const ALL: [u32; 3] = [1, 2, 3];
you could write
const ALL: &'static [u32] = &[1, 2, 3];
Thus your macro should be
const ALL_VARIANTS: &'static [$Name] = &[$($Name::$Variant),*];
To answer the other question ("how to count with macros"): it's a simple head-tail-kind-of-algorithm:
macro_rules! count {
($head:ident $(, $tail:ident)*) => { 1 + count!($($tail),*) };
() => { 0 };
}
println!("{}", count!()); // 0
println!("{}", count!(A)); // 1
println!("{}", count!(A, B, C, D)); // 4

Is there a way to count with macros?

I want to create a macro that prints "Hello" a specified number of times. It's used like:
many_greetings!(3); // expands to three `println!("Hello");` statements
The naive way to create that macro is:
macro_rules! many_greetings {
($times:expr) => {{
println!("Hello");
many_greetings!($times - 1);
}};
(0) => ();
}
However, this doesn't work because the compiler does not evaluate expressions; $times - 1 isn't calculated, but fed as a new expression into the macro.
While the ordinary macro system does not enable you to repeat the macro expansion many times, there is no problem with using a for loop in the macro:
macro_rules! many_greetings {
($times:expr) => {{
for _ in 0..$times {
println!("Hello");
}
}};
}
If you really need to repeat the macro, you have to look into procedural macros/compiler plugins (which as of 1.4 are unstable, and a bit harder to write).
Edit: There are probably better ways of implementing this, but I've spent long enough on this for today, so here goes. repeat!, a macro that actually duplicates a block of code a number of times:
main.rs
#![feature(plugin)]
#![plugin(repeat)]
fn main() {
let mut n = 0;
repeat!{ 4 {
println!("hello {}", n);
n += 1;
}};
}
lib.rs
#![feature(plugin_registrar, rustc_private)]
extern crate syntax;
extern crate rustc;
use syntax::codemap::Span;
use syntax::ast::TokenTree;
use syntax::ext::base::{ExtCtxt, MacResult, MacEager, DummyResult};
use rustc::plugin::Registry;
use syntax::util::small_vector::SmallVector;
use syntax::ast::Lit_;
use std::error::Error;
fn expand_repeat(cx: &mut ExtCtxt, sp: Span, tts: &[TokenTree]) -> Box<MacResult + 'static> {
let mut parser = cx.new_parser_from_tts(tts);
let times = match parser.parse_lit() {
Ok(lit) => match lit.node {
Lit_::LitInt(n, _) => n,
_ => {
cx.span_err(lit.span, "Expected literal integer");
return DummyResult::any(sp);
}
},
Err(e) => {
cx.span_err(sp, e.description());
return DummyResult::any(sp);
}
};
let res = parser.parse_block();
match res {
Ok(block) => {
let mut stmts = SmallVector::many(block.stmts.clone());
for _ in 1..times {
let rep_stmts = SmallVector::many(block.stmts.clone());
stmts.push_all(rep_stmts);
}
MacEager::stmts(stmts)
}
Err(e) => {
cx.span_err(sp, e.description());
DummyResult::any(sp)
}
}
}
#[plugin_registrar]
pub fn plugin_registrar(reg: &mut Registry) {
reg.register_macro("repeat", expand_repeat);
}
added to Cargo.toml
[lib]
name = "repeat"
plugin = true
Note that if we really don't want to do looping, but expanding at compile-time, we have to do things like requiring literal numbers. After all, we are not able to evaluate variables and function calls that reference other parts of the program at compile time.
As the other answers already said: no, you can't count like this with declarative macros (macro_rules!).
But you can implement the many_greetings! example as a procedural macro. procedural macros were stabilized a while ago, so the definition works on stable. However, we can't yet expand macros into statements on stable -- that's what the #![feature(proc_macro_hygiene)] is for.
This looks like a lot of code, but most code is just error handling, so it's not that complicated!
examples/main.rs
#![feature(proc_macro_hygiene)]
use count_proc_macro::many_greetings;
fn main() {
many_greetings!(3);
}
Cargo.toml
[package]
name = "count-proc-macro"
version = "0.1.0"
authors = ["me"]
edition = "2018"
[lib]
proc-macro = true
[dependencies]
quote = "0.6"
src/lib.rs
extern crate proc_macro;
use std::iter;
use proc_macro::{Span, TokenStream, TokenTree};
use quote::{quote, quote_spanned};
/// Expands into multiple `println!("Hello");` statements. E.g.
/// `many_greetings!(3);` will expand into three `println`s.
#[proc_macro]
pub fn many_greetings(input: TokenStream) -> TokenStream {
let tokens = input.into_iter().collect::<Vec<_>>();
// Make sure at least one token is provided.
if tokens.is_empty() {
return err(Span::call_site(), "expected integer, found no input");
}
// Make sure we don't have too many tokens.
if tokens.len() > 1 {
return err(tokens[1].span(), "unexpected second token");
}
// Get the number from our token.
let count = match &tokens[0] {
TokenTree::Literal(lit) => {
// Unfortunately, `Literal` doesn't have nice methods right now, so
// the easiest way for us to get an integer out of it is to convert
// it into string and parse it again.
if let Ok(count) = lit.to_string().parse::<usize>() {
count
} else {
let msg = format!("expected unsigned integer, found `{}`", lit);
return err(lit.span(), msg);
}
}
other => {
let msg = format!("expected integer literal, found `{}`", other);
return err(other.span(), msg);
}
};
// Return multiple `println` statements.
iter::repeat(quote! { println!("Hello"); })
.map(TokenStream::from)
.take(count)
.collect()
}
/// Report an error with the given `span` and message.
fn err(span: Span, msg: impl Into<String>) -> TokenStream {
let msg = msg.into();
quote_spanned!(span.into()=> {
compile_error!(#msg);
}).into()
}
Running cargo run --example main prints three "Hello"s.
For those looking for a way to do this, there is also the seq_macro crate.
It is fairly easy to use and works out of the box with stable Rust.
use seq_macro::seq;
macro_rules! many_greetings {
($times:literal) => {
seq!{ N in 0..$times {
println!("Hello");
}}
};
}
fn main() {
many_greetings!(3);
many_greetings!(12);
}
As far as I know, no. The macro language is based on pattern matching and variable substitution, and only evaluates macros.
Now, you can implement counting with evaluation: it just is boring... see the playpen
macro_rules! many_greetings {
(3) => {{
println!("Hello");
many_greetings!(2);
}};
(2) => {{
println!("Hello");
many_greetings!(1);
}};
(1) => {{
println!("Hello");
many_greetings!(0);
}};
(0) => ();
}
Based on this, I am pretty sure one could invent a set of macro to "count" and invoke various operations at each step (with the count).

Macro that generates a function with arguments determined by the macro

Is it possible to write a macro that generates a function where the number of arguments to this function to be a determined by the macro? For instance, I'd like to write something to make using prepared statements in the Cassandra driver easier.
let prepared = prepare!(session, "insert into blah (id, name, reading ) values (?, ?, ?)", int, string, float);
let stmt = prepared(1, "test".to_string(), 3.1);
session.execute(stmt);
prepare! would need to generate something like (unwrap only here for brevity):
fn some_func(arg1, arg2, arg3) -> Statement {
let mut statement = Statement::new("insert into blah (id, name, reading ) values (?, ?, ?)", 3);
statement.bind_int(0, arg1).unwrap()
.bind_string(1, arg2).unwrap()
.bind_float(2, arg3).unwrap()
}
Two hard things in Rust macros: counting and unique identifers. You have both. Then again, I'm the one writing the answer, so I suppose it's my problem now. At least you didn't ask about parsing the string (which is outright impossible without compiler plugins).
Another impossible thing is mapping types to different methods. You just can't. Instead, I'm going to assume the existence of a helper trait that does this mapping.
Also, Rust doesn't have int, string, or float. I assume you mean i32, String, and f32.
Finally, the way you've written the invocation and expansion don't really gel. I don't see why session is involved; it's not used in the expansion. So I'm going to take the liberty of just pretending you don't need it; if you do, you'll have to hack it back in.
So, with that, here's what I came up with.
// Some dummy types so the following will type-check.
struct Statement;
impl Statement {
fn new(stmt: &str, args: usize) -> Self { Statement }
fn bind_int(self, pos: usize, value: i32) -> Result<Self, ()> { Ok(self) }
fn bind_float(self, pos: usize, value: f32) -> Result<Self, ()> { Ok(self) }
fn bind_string(self, pos: usize, value: String) -> Result<Self, ()> { Ok(self) }
}
struct Session;
impl Session {
fn execute(&self, stmt: Statement) {}
}
// The supporting `BindArgument` trait.
trait BindArgument {
fn bind(stmt: Statement, pos: usize, value: Self) -> Statement;
}
impl BindArgument for i32 {
fn bind(stmt: Statement, pos: usize, value: Self) -> Statement {
stmt.bind_int(pos, value).unwrap()
}
}
impl BindArgument for f32 {
fn bind(stmt: Statement, pos: usize, value: Self) -> Statement {
stmt.bind_float(pos, value).unwrap()
}
}
impl BindArgument for String {
fn bind(stmt: Statement, pos: usize, value: Self) -> Statement {
stmt.bind_string(pos, value).unwrap()
}
}
// The macro itself.
macro_rules! prepare {
// These three are taken straight from
// https://danielkeep.github.io/tlborm/book/
(#as_expr $e:expr) => {$e};
(#count_tts $($tts:tt)*) => {
<[()]>::len(&[$(prepare!(#replace_tt $tts ())),*])
};
(#replace_tt $_tt:tt $e:expr) => {$e};
// This is how we bind *one* argument.
(#bind_arg $stmt:expr, $args:expr, $pos:tt, $t:ty) => {
prepare!(#as_expr <$t as BindArgument>::bind($stmt, $pos, $args.$pos))
};
// This is how we bind *N* arguments. Note that because you can't do
// arithmetic in macros, we have to spell out every supported integer.
// This could *maybe* be factored down with some more work, but that
// can be homework. ;)
(#bind_args $stmt:expr, $args:expr, 0, $next:ty, $($tys:ty,)*) => {
prepare!(#bind_args prepare!(#bind_arg $stmt, $args, 0, $next), $args, 1, $($tys,)*)
};
(#bind_args $stmt:expr, $args:expr, 1, $next:ty, $($tys:ty,)*) => {
prepare!(#bind_args prepare!(#bind_arg $stmt, $args, 1, $next), $args, 2, $($tys,)*)
};
(#bind_args $stmt:expr, $args:expr, 2, $next:ty, $($tys:ty,)*) => {
prepare!(#bind_args prepare!(#bind_arg $stmt, $args, 2, $next), $args, 3, $($tys,)*)
};
(#bind_args $stmt:expr, $_args:expr, $_pos:tt,) => {
$stmt
};
// Finally, the entry point of the macro.
($stmt:expr, $($tys:ty),* $(,)*) => {
{
// I cheated: rather than face the horror of trying to *also* do
// unique identifiers, I just shoved the arguments into a tuple, so
// that I could just re-use the position.
fn prepared_statement(args: ($($tys,)*)) -> Statement {
let statement = Statement::new(
$stmt,
prepare!(#count_tts $(($tys))*));
prepare!(#bind_args statement, args, 0, $($tys,)*)
}
prepared_statement
}
};
}
fn main() {
let session = Session;
let prepared = prepare!(
r#"insert into blah (id, name, reading ) values (?, ?, ?)"#,
i32, String, f32);
// Don't use .to_string() for &str -> String; it's horribly inefficient.
let stmt = prepared((1, "test".to_owned(), 3.1));
session.execute(stmt);
}
And here's what the main function expands to, to give you a frame of reference:
fn main() {
let session = Session;
let prepared = {
fn prepared_statement(args: (i32, String, f32)) -> Statement {
let statement = Statement::new(
r#"insert into blah (id, name, reading ) values (?, ?, ?)"#,
<[()]>::len(&[(), (), ()]));
<f32 as BindArgument>::bind(
<String as BindArgument>::bind(
<i32 as BindArgument>::bind(
statement, 0, args.0),
1, args.1),
2, args.2)
}
prepared_statement
};
// Don't use .to_string() for &str -> String; it's horribly inefficient.
let stmt = prepared((1, "test".to_owned(), 3.1));
session.execute(stmt);
}

Can a Rust macro create new identifiers?

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.