How to use multiple macros inside macros in Rust? - rust

I’d like to have a code that supposed to achieve this in end.
counters!(NAME1, “unit1”, NAME2, “unit2”)
// generates
const NAME1 = 0;
const NAME2 = 1;
static mut counters = [AtomicUsize::new(0), AtomicUsize::new(0)];
static units = [“unit1”, “unit2”];
So far I’ve been able to create indices names and the array of arbitrary values, but I have troubles combining this things together.
playgound link
use std::sync::atomic::AtomicUsize;
macro_rules! gen_array {
($out:expr; ) => { $out };
([$($out:tt)*]; $name:ident, $($names:tt)*) => {
gen_array!([$($out)* $name,]; $($names)*)
};
}
macro_rules! gen_vars {
($cnt:expr; ) => {};
($cnt:expr; $name:ident, $($names:tt)*) => {
const $name: usize = $cnt;
gen_vars!($cnt + 1; $($names)*)
};
}
macro_rules! counters {
($($name:ident),+) => {
gen_vars!(0; $($name),+,);
gen_array!([]; $($name),+,);
};
}
fn main() {
let arr = counters!(ONE, TWO);
dbg!(arr);
}

This seems to do what you want.
use std::sync::atomic::AtomicUsize;
macro_rules! gen_array {
($out:expr; ) => { $out };
([$($out:tt)*]; $name:literal, $($names:tt)*) => {
gen_array!([$($out)* $name,]; $($names)*)
};
}
macro_rules! gen_vars {
($cnt:expr; ) => {};
($cnt:expr; $name:ident, $($names:tt)*) => {
const $name: usize = $cnt;
gen_vars!($cnt + 1; $($names)*)
};
}
macro_rules! counters {
($($name:ident,$value:literal),+) => {
gen_vars!(0; $($name),+,);
gen_array!([]; $($value),+,);
};
}
fn main() {
counters!(ONE, "unit1", TWO, "unit2");
}
Playground

Related

Is it possible to make this function more readable?

I have this function:
fn init_clients() -> (upload_executor::Executor, download_executor::Executor) {
let client_fs = if USE_CLOUD {
None
} else {
Some(Arc::new(adapter_fs::Client::new(".")))
};
let client_cloud = if USE_CLOUD {
Some(Arc::new(
adapter_s3::Client::new(
"https://s3.region.aws.com",
"region",
"bucket_name",
"KEY_ID",
"KEY_SECRET",
)
.unwrap(),
))
} else {
None
};
let client = match USE_CLOUD {
true => client_cloud
.as_ref()
.map(|o| o.clone() as Arc<dyn AdapterTrait>)
.unwrap(),
false => client_fs
.as_ref()
.map(|o| o.clone() as Arc<dyn AdapterTrait>)
.unwrap(),
};
let upload_executor = upload_executor::Executor::new(client.clone());
let download_executor = download_executor::Executor::new(client);
(upload_executor, download_executor)
}
I would like to know how to make it more readable, like:
let upload_client: TraitA + TraitB = if USE_CLOUD {
upload_client = fs::new()
} else {
upload_client = s3::new()
}
Is it possible?
If you merged the sections together you could create something like this. Also keep in mind that I haven't checked this in an IDE so I am not completely sure if I correctly converted to an anonymous trait.
fn init_s3_client() -> Arc<adapter_s3::Client> {
let client = adapter_s3::Client::new(
"https://s3.region.aws.com",
"region",
"bucket_name",
"KEY_ID",
"KEY_SECRET");
match client {
Ok(x) => Arc::new(x),
Err(err) => panic!("Failed to create s3 client: {:?}", err),
}
}
fn init_clients() -> (upload_executor::Executor, download_executor::Executor) {
let client = match USE_CLOUD {
true => init_s3_client() as Arc<dyn AdapterTrait>,
false => Arc::new(adapter_fs::Client::new(".")) as Arc<dyn AdapterTrait>,
};
let upload_executor = upload_executor::Executor::new(client.clone());
let download_executor = download_executor::Executor::new(client);
(upload_executor, download_executor)
}

Extracting the index of occurrence of a field in a struct-generating macro

Is there any way to extract the "index" (order of occurrence) of a struct field in a macro like this:
macro_rules! example {
(struct $name:ident {
$($field_name:ident: $field_type:ty,)*
}) => {
struct $name {
$($field_name: $field_type,)*
}
impl $name {
fn print_members(&self) {
$(println!("{} {} {}",
stringify!($field_name),
stringify!($field_type),
stringify!(<<<FIELD INDEX SOMEHOW>>>),
);)*
}
}
}
}
such that
example! {
struct SomeStruct {
a: String,
b: String,
c: usize,
}
}
would print:
a String 0
b String 1
c usize 2
I'm pretty sure a proc macro can achieve that (not that I knew how to write one), but is it possible with declarative macros?
On nightly this is easy, using #![feature(macro_metavar_expr)]:
#![feature(macro_metavar_expr)]
macro_rules! example {
(struct $name:ident {
$($field_name:ident: $field_type:ty,)*
}) => {
struct $name {
$($field_name: $field_type,)*
}
impl $name {
fn print_members(&self) {
$(println!("{} {} {}",
stringify!($field_name),
stringify!($field_type),
stringify!(${index()}),
);)*
}
}
}
}
On stable, you can count using TT munching. You can no longer use stringify!(), however, because this will create a sequence 0 + 1 + 1 + 1 + ... unexpanded. Since you are just printing, you can use the number directly:
macro_rules! example {
(struct $name:ident {
$($field_name:ident: $field_type:ty,)*
}) => {
struct $name {
$($field_name: $field_type,)*
}
impl $name {
fn print_members(&self) {
example! { #generate_println
[ 0 ]
$( $field_name : $field_type, )*
}
}
}
};
{ #generate_println
[ $index:expr ]
} => {
// Stop condition.
};
{ #generate_println
[ $index:expr ]
$first_field_name:ident : $first_field_type:ty,
$( $rest_field_name:ident : $rest_field_type:ty, )*
} => {
println!("{} {} {}",
stringify!($first_field_name),
stringify!($first_field_type),
$index,
);
example! { #generate_println
[ $index + 1 ]
$( $rest_field_name : $rest_field_type, )*
}
};
}

Is there a simple way to move clone of Rc into closure?

I need to move a clone of Rc value into several closures. Is there a better way to do it rather than cloning it before every closure?
let val = Rc::new(my_val);
let val_clone = val.clone();
closure_func1(move || { do_stuff1(val_clone) });
let val_clone = val.clone();
closure_func2(move || { do_stuff2(val_clone) });
let val_clone = val.clone();
closure_func3(move || { do_stuff3(val_clone) });
You could create a utility function to remove the repetition:
fn with_cloned<T>(rc: &Rc<T>, f: impl FnOnce(Rc<T>)) -> impl FnOnce() {
let rc = Rc::clone(rc);
move || f(rc)
}
let val = Rc::new(my_val);
closure_func1(with_cloned(&val, |val_clone| do_stuff1(val_clone)));
closure_func2(with_cloned(&val, |val_clone| do_stuff2(val_clone)));
closure_func3(with_cloned(&val, |val_clone| do_stuff3(val_clone)));
I found this little macro in the comments under this issue:
macro_rules! enclose {
( ($( $x:ident ),*) $y:expr ) => {
{
$(let $x = $x.clone();)*
$y
}
};
}
which you can use like this:
let val = Rc::new("Hello World".to_string());
closure_func1(enclose! { (val) move || { do_stuff1(val) } });
closure_func2(enclose! { (val) move || { do_stuff2(val) } });
closure_func3(enclose! { (val) move || { do_stuff3(val) } });
Playground Example
It is slightly more general than user4815162342's solution in that you can specify any number of values of any type as long as they can be cloned.
I also found the closure crate which allows you to specify any combination of moves, clones and others. You would use that like this:
let val = Rc::new("Hello World".to_string());
closure_func1(closure!(clone val, || { do_stuff1(val) } ));
closure_func2(closure!(clone val, || { do_stuff2(val) } ));
closure_func3(closure!(clone val, || { do_stuff3(val) } ));

Creating a Custom Colored dbg! Macro In Rust

I'd like to create a custom macro similar to the standard dbg! macro, but with the option to use colors via the colored crate. dbg! usually prints something with the format of
[path_to_file:line_number] "symbol name" = "symbol value"
//[src/gallery/image_slot.rs:231] "my_integer_value_of_12" = "12"
How do I access the path/line number [path_to_file:line_number] so I can print it?
How do I access the symbol name of a variable? (i.e. print my_var given my_var = 12)
Use the file!, line!, and column! macros.
Use the stringify! macro.
If you go to the docs of the dbg! macro, you can click [src], which shows the implementation of dbg!, which is as follows:
macro_rules! dbg {
() => {
$crate::eprintln!("[{}:{}]", $crate::file!(), $crate::line!());
};
($val:expr $(,)?) => {
// Use of `match` here is intentional because it affects the lifetimes
// of temporaries - https://stackoverflow.com/a/48732525/1063961
match $val {
tmp => {
$crate::eprintln!("[{}:{}] {} = {:#?}",
$crate::file!(), $crate::line!(), $crate::stringify!($val), &tmp);
tmp
}
}
};
($($val:expr),+ $(,)?) => {
($($crate::dbg!($val)),+,)
};
}
Using that, we can easily create a similar colored_dbg! macro, with the colored crate as you suggested.
(I just picked random colors, for a simple example)
// colored = "2.0"
use colored::Colorize;
macro_rules! colored_dbg {
() => {
eprintln!("{}", format!("[{}:{}]", file!(), line!()).green());
};
($val:expr $(,)?) => {
match $val {
tmp => {
eprintln!("{} {} = {}",
format!("[{}:{}]", file!(), line!()).green(),
stringify!($val).red(),
format!("{:#?}", &tmp).blue(),
);
tmp
}
}
};
($($val:expr),+ $(,)?) => {
($(colored_dbg!($val)),+,)
};
}
You'd use it just like how you'd be able to use dbg!:
fn main() {
let my_var = 12;
colored_dbg!(&my_var);
let v = vec!["foo", "bar", "baz"];
let v = colored_dbg!(v);
}
Which outputs the following:

Rust macro: capture exactly matching tokens

My goal is to write a macro expand! such that:
struct A;
struct B;
struct Mut<T>;
expand!() => ()
expand!(A) => (A,)
expand!(mut A) => (Mut<A>,)
expand!(A, mut B) => (A, Mut<B>,)
// etc
[Edit] added trailing comma for consistent tuple syntax.
I wrote this macro so far:
macro_rules! to_type {
( $ty:ty ) => { $ty };
( mut $ty:ty ) => { Mut<$ty> };
}
macro_rules! expand {
( $( $(mut)? $ty:ty ),* ) => {
(
$( to_type!($ty) ),*
,)
};
}
What I'm struggling with, is capturing the mut token. How can I assign it to a variable and reuse it in the macro body? Is it possible to work on more than 1 token at a time?
Something like this?
macro_rules! expand {
(#phase2($($ty_final:ty),*),) => {
($($ty_final,)*)
};
(#phase2($($ty_final:ty),*), mut $ty:ty, $($rest:tt)*) => {
expand!(#phase2($($ty_final,)* Mut::<$ty>), $($rest)*)
};
(#phase2($($ty_final:ty),*), $ty:ty, $($rest:tt)*) => {
expand!(#phase2($($ty_final,)* $ty), $($rest)*)
};
($($t:tt)*) => {
expand!(#phase2(), $($t)*)
};
}
struct A;
struct B;
struct Mut<T>(std::marker::PhantomData<T>);
fn main() {
#[allow(unused_parens)]
let _: expand!() = ();
#[allow(unused_parens)]
let _: expand!(A,) = (A,);
#[allow(unused_parens)]
let _: expand!(mut B,) = (Mut::<B>(Default::default()),);
#[allow(unused_parens)]
let _: expand!(A, mut B,) = (A, Mut::<B>(Default::default()));
}

Resources