I'm just getting going with Rust, and am trying to smooth over a couple inconveniences using macros. The first is that the derive Defaults macros don't support const functions so they don't support static objects. For this, I got help and adapted this macro I was given, it works well:
#[macro_export]
macro_rules! define_struct_with_const_defaults {
($v:vis struct $name:ident { $($f_name:ident: $f_type:ty = $f_default:expr),* $(,)? }) => {
#[derive(Clone,Copy)]
$v struct $name {
$(
$f_name: $f_type,
)*
}
impl $name {
pub const fn new() -> $name {
$name {
$(
$f_name: $f_default,
)*
}
}
}
}
}
So the next thing I wanted to do was get painless structure access for unit testing with python going. So I thought I could do something like this:
use pyo3::prelude::*;
#[macro_export]
macro_rules! define_struct_with_const_defaults {
($v:vis struct $name:ident { $($f_name:ident: $f_type:ty = $f_default:expr),* $(,)? }) => {
#[derive(Clone,Copy)]
#[pyclass]
$v struct $name {
$(
#[pyo3(get, set)] $f_name: $f_type,
)*
}
impl $name {
pub const fn new() -> $name {
$name {
$(
$f_name: $f_default,
)*
}
}
}
}
}
But that doesn't work, the complaint is "cannot find attribute pyclass in this scope"
On the other hand, if I do this, it works:
use pyo3::prelude::*;
#[pyclass]
struct bar {
baz: i32,
}
This is beyond my level of understanding. It seems to be related to nesting macros, I'm wondering whether it has something to do with order of evaluation.
Eventually, I'd like to conditionally compile the pyo3 stuff with a config macro, depending on whether I'm in a unit test environment, but I can't even get this far ...
Any help would be appreciated in understanding what is going on here.
The problem is probably that the macro will insert #[pyclass] at the callsite of it. So you have to put your use pyo3::prelude::*; there or even better include the full path in the macro to avoid name conflicts:
#[macro_export]
macro_rules! define_struct_with_const_defaults {
($v:vis struct $name:ident { $($f_name:ident: $f_type:ty = $f_default:expr),* $(,)? }) => {
#[derive(Clone,Copy)]
#[::pyo3::prelude::pyclass]
$v struct $name {
$(
#[pyo3(get, set)] $f_name: $f_type,
)*
}
impl $name {
pub const fn new() -> $name {
$name {
$(
$f_name: $f_default,
)*
}
}
}
}
}
Related
I have two versions of a structure, one is enabled when a feature is enabled, the other when its not enabled, as shown below. The thing is that one contains unsafe methods, the other does not. So when these functions are called in other parts of the library, I have surrounded them with an unsafe { } block. When my library is compiled without the feature enabled, I get the following warning:
unnecessary unsafe block
How do I get around / disable this warning when its not applicable, ie. when the feature is not enabled.
#[cfg(feature = "my-feature")]
pub struct MyStruct {
// some fields
}
#[cfg(not(feature = "my-feature"))]
pub struct MyStruct {
// some different fields
}
#[cfg(feature = "my-feature")]
impl MyStruct {
pub unsafe fn my_func() {}
}
#[cfg(not(feature = "my-feature"))]
impl MyStruct {
pub fn my_func() {}
}
fn main() {
// I want to get rid of this unsafe when its not needed.
unsafe { MyStruct::my_func() };
}
If these cases are only inside your library, so you can afford little incovenience, you can use a macro:
macro_rules! call {
// For static methods.
{
unsafe {
$($method:ident)::+ ( $($arg:expr),* $(,)? )
}
} => {{
#[cfg(feature = "my-feature")]
let v = unsafe { $($method)::+ ( $($arg),* ) };
#[cfg(not(feature = "my-feature"))]
let v = $($method)::+ ( $($arg),* );
v
}};
// For instance methods.
{
unsafe {
$object:ident . $method:ident ( $($arg:expr),* $(,)? )
}
} => {{
#[cfg(feature = "my-feature")]
let v = unsafe { $object . $method ( $($arg),* ) };
#[cfg(not(feature = "my-feature"))]
let v = $object . $method ( $($arg),* );
v
}};
}
call!(unsafe { MyStruct::my_func() });
call!(unsafe { v.my_method() });
This macro does not allow generics or complex expression as the receiver. Extending it to support generics should be fairly easy; supporting complex expressions will probably require a proc macro.
There is a better way to write the macro: instead of requiring unsafe inside it, and wrapping the call in cfg, just do something unsafe inside it, thus always requiring unsafe outside of it. This also solves the problem with generics and complex receivers:
pub(crate) unsafe fn always_unsafe() {}
macro_rules! call {
($($call:tt)+) => {{
$crate::always_unsafe();
$($call)*
}};
}
unsafe {
call!(MyStruct::my_func());
}
unsafe {
call!(v.my_method());
}
I try to generate a struct from a macro looking like that:
decl_struct! {
MyStruct {
mut foo,
const bar,
}
}
The thing is that I want to filter out the fields marked as const. I have tried with the following code, but it does not function due to the fact that the outer macro is evaluated first:
macro_rules! decl_struct {
{
$name:ident {
$( $kind:ident $member:ident, )*
}
} => {
decl_struct!(#gen_struct $name { $( decl_struct!(#gen_struct_member $kind $member) )* } );
};
// Filters the `const` members out:
(#gen_struct_member mut $member:ident) => ($member,);
(#gen_struct_member const $member:ident) => ();
// Generate the whole struct:
{
#gen_struct $name:ident {
$( $member:ident, )*
}
} => {
struct $name {
$( $member:ident : u32 /*Dummy type*/, )*
}
};
}
decl_struct! {
MyStruct {
mut foo,
const bar,
}
}
Link to the playground.
What can be an alternative approach?
As documented in the reference:
Macros can expand to expressions, statements, items (including traits, impls, and foreign items), types, or patterns.
Notably, they can't expand to singular field definitions (as attempted by your #gen_struct_member invocations). However, for your particular problem, you can instead simplify to a single macro invocation with a bit of clever repetition:
macro_rules! decl_struct {
{
$name:ident {
$( const $initial:ident, )*
$(
mut $mut:ident,
$( const $const:ident, )*
)*
}
} => {
struct $name {$(
$mut: u32,
)*}
};
}
Playground.
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 write a macro that defines an enum which wraps an arbitrary number of (distinct) input types?
I'd like to do a kind of type-level match.
type_switch!(i32 => println!("integer"), f32 => println!("float"), Foo => println!("foo"))
This would expand to:
{
enum Wrapper {
Variant1(i32),
Variant2(f32),
Variant3(Foo),
}
// impl From<i32>, From<f32>, From<Foo> for Wrapper
|x: Wrapper| match x {
Wrapper::Variant1(x) => println!("integer"),
Wrapper::Variant2(x) => println!("float"),
Wrapper::Variant3(x) => println!("foo"),
}
}
so that I can write like
let switch = type_switch!(i32 => println!("integer"), f32 => println!("float"), Foo => println!("foo"));
switch(32.into()); // prints "integer"
switch(24.0.into()); // prints "float"
Define a trait within your macro and implement it for each type:
macro_rules! type_switch {
($($ty: ty => $expr: expr),+) => {{
trait TypeMatch {
fn type_match(self);
}
$(
impl TypeMatch for $ty {
fn type_match(self) {
$expr
}
}
)+
TypeMatch::type_match
}}
}
Notice that the first time you call the function the compiler will bind the type so that subsequent calls must be the same type:
struct Foo;
fn main() {
let s = type_switch! {
i32 => { println!("i32"); },
f32 => { println!("f32"); },
Foo => { println!("Foo"); }
};
s(0);
s(Foo); // Error!
}
If you need to be able to call it with different types, this can be fixed (at a small cost) by using a trait object for dynamic dispatch:
macro_rules! type_switch {
($($ty: ty => $expr: expr),+) => {{
trait TypeMatch {
fn type_match(&self);
}
$(
impl TypeMatch for $ty {
fn type_match(&self) {
$expr
}
}
)+
|value: &dyn TypeMatch| {
value.type_match()
}
}}
}
struct Foo;
fn main() {
let s = type_switch! {
i32 => { println!("i32"); },
f32 => { println!("f32"); },
Foo => { println!("Foo"); }
};
s(&0);
s(&Foo);
}
Also notice that you have to pass references instead of values.
It can make sense to write wrapper types as you have proposed, but only if the type is needed in larger parts of your code.
Your specific example would define a new enum every time you use the macro, move the value into the new enum and then immediately throw it away.
That's not a idiomatic approach and if that is indeed your imagined use I'd recommend looking for different options.
That said, I have used wrapper types on a number of occasions.
Something like this will work for declaring a wrapper:
macro_rules! declare_wrapper {
(
$enum_name:ident {
$( $variant_name:ident( $typ:ty : $description:expr ) ),*
}
)=> {
pub enum $enum_name {
$(
$variant_name($typ),
)*
}
$(
impl From<$typ> for $enum_name {
fn from(value: $typ) -> Self {
$enum_name::$variant_name(value)
}
}
)*
impl $enum_name {
fn describe(&self) -> &'static str {
match self {
$(
&$enum_name::$variant_name(_) => $description,
)*
}
}
}
};
}
declare_wrapper!( MyWrapper {
MyInt(i64 : "int"),
MyString(String : "string")
});
fn main() {
let value = MyWrapper::from(22);
println!("{}", value.describe());
}
You can also extend this to add additional methods or trait impls that you need.
I've done similar things quite often.
To learn Rust, I have started to implement some of the Project Euler problems. Now I want to take the next step and create a console based user interface, which has the ability for running all or only specific problems. Another requirement is that the user should be able to pass optional parameters only to a specific problem.
My current solution is to have a Trait ProjectEulerProblem that declares for example run(). With that I can do something like this:
fn main() {
let args: Args = Args::docopt().decode().unwrap_or_else(|e| e.exit());
let problems: Vec<Box<problems::ProjectEulerProblem>> = vec![
box problems::Problem1,
box problems::Problem2
];
match args.flag_problem {
Some(x) => println!("Result of problem: {} is {}", x, problems[x-1].run()),
None => println!("No problem number given.")
}
}
My question is, is there a way to get rid of the explicit problems vector initialization, maybe by using macros? Alternative ideas for implementing my application like described above are also welcome.
You can use a macro with repetition to generate your list without having to type out the full path and name every time.
macro_rules! problem_vec(
($( $prob:tt ),*) => ({
&[
$(
&concat_idents!(Proble, $prob),
)*
]
});
);
const PROBLEMS: &'static[&'static ProjectEulerProblem] = problem_vec!(m1, m2);
Note, you cannot simply use indices, because the concat_idents macro requires an identifier and numbers are not identifiers. concat_idents is also only available on nightly. On stable you need to give the entire struct name:
macro_rules! problem_vec(
($( $prob:ident ),*) => ({
&[
$(
&problems::$prob,
)*
]
});
);
const PROBLEMS: &'static [&'static problems::ProjectEulerProblem] = problem_vec!(
Problem1, Problem2
);
PlayPen
My mashup crate lets you define a concise way to build the problems array as problems![1, 2]. This approach works with any Rust version >= 1.15.0.
#[macro_use]
extern crate mashup;
mod problems {
pub trait ProjectEulerProblem {
fn run(&self);
}
pub struct Problem1;
impl ProjectEulerProblem for Problem1 {
fn run(&self) {
println!("running Project Euler problem 1");
}
}
pub struct Problem2;
impl ProjectEulerProblem for Problem2 {
fn run(&self) {
println!("running Project Euler problem 2");
}
}
}
macro_rules! problems {
($($number:tt),*) => {{
// Use mashup to define a substitution macro `m!` that replaces every
// occurrence of the tokens `"Problem" $number` in its input with the
// concatenated identifier `Problem $number`.
mashup! {
$(
m["Problem" $number] = Problem $number;
)*
}
// Invoke the substitution macro to build a slice of problems. This
// expands to:
//
// &[
// &problems::Problem1 as &problems::ProjectEulerProblem,
// &problems::Problem2 as &problems::ProjectEulerProblem,
// ]
m! {
&[
$(
&problems::"Problem" $number as &problems::ProjectEulerProblem,
)*
]
}
}}
}
fn main() {
for p in problems![1, 2] {
p.run();
}
}