Can a procedural macro derive on a struct add other derives? - macros

Is it possible for a procedural macro derive to add derives from other crates to the struct that it is derived upon?
lib.rs
#[derive(Combined)]
struct Foo;
derive_combined.rs
#[macro_use] extern crate quote;
extern crate proc_macro2;
extern crate proc_macro;
extern crate syn;
use proc_macro::TokenStream;
#[proc_macro_derive(Combined)]
pub fn my_macro(input: TokenStream) -> TokenStream {
let input: DeriveInput = syn::parse(input).unwrap();
let ident = input.ident;
let expanded = quote! {
#[derive(Clone, Debug)]
struct #ident;
};
expanded.into()
}

While this cannot be done conveniently with proc_macro_derive it can be done with proc_macro_attribute and seeing as the other answer already uses a derive attribute this solution may be better for your use case:
extern crate proc_macro;
extern crate proc_macro2;
#[macro_use]
extern crate quote;
extern crate syn;
use proc_macro2::TokenStream;
#[proc_macro_attribute]
pub fn add_derive(_metadata: proc_macro::TokenStream, input: proc_macro::TokenStream)
-> proc_macro::TokenStream {
let input: TokenStream = input.into();
let output = quote! {
#[derive(Debug, Serialize, Deserialize, etc, ...)]
#input
};
output.into()
}
Then to use this macro:
#[add_derive]
pub struct TestStruct { ... }
Notably, attribute macros replace token streams whereas derive macros are suited for appending to token streams: Rust Reference: Procedural Macros

With some sub-optimal work-arounds - yes!
The first issue I encountered while implementing this was the duplicate definitions for the struct - having more than one definition just won't work. To get around this, I used a custom attribute that must be specified which will be the name of the struct in the generated code:
#![feature(custom_attribute)]
#[macro_use] extern crate quote;
extern crate proc_macro;
extern crate proc_macro2;
extern crate syn;
use syn::DeriveInput;
use proc_macro::TokenStream;
use proc_macro2::{Ident, Span};
use syn::{Attribute, Meta, Lit};
#[proc_macro_derive(Combined)]
#[attribute(ActualName)]
pub fn my_macro(input: TokenStream) -> TokenStream {
let mut input: DeriveInput = syn::parse(input).unwrap();
for attr in input.attrs.iter().map(Attribute::interpret_meta).filter(|x| x.is_some()).map(|x| x.unwrap()) {
if &attr.name().to_string()[..] != "ActualName" { continue }
let name;
match attr {
Meta::Word(ident) => { panic!("ActualName must be a name-value pair (i.e. #[ActualName = \"hey\"])"); },
Meta::List(_) => { panic!("ActualName must be a name-value pair (i.e. #[ActualName = \"hey\"])"); },
Meta::NameValue(meta_name_value) => {
match meta_name_value.lit {
Lit::Str(lit_str) => { name = lit_str.value(); },
_ => { panic!("ActualName must be a string"); }
};
}
};
input.ident = Ident::new(&name[..], Span::call_site());
let expanded = quote! {
#[derive(Clone, Debug)]
#input
};
return expanded.into()
}
panic!("You must specify the ActualName attribute (i.e. #[Derive(Combined), ActualName = \"...\"]")
}
After putting this code in your derive crate, the following sample of code will work:
#![feature(custom_attribute)]
#[macro_use]
extern crate derive_combined;
#[derive(Combined)]
#[ActualName = "Stuff"]
struct Stuff_ {
pub a: i32,
pub b: i64,
}
fn main() {
println!("{:?}", Stuff { a: 10, b: 10 }.clone());
}
If you have any questions about implementing this, this is the tutorial I followed. If that doesn't help feel free to ask.

Related

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 do I generalize a Rust macro over different types of functions?

I have a macro that takes a list of function declarations and turns them into different declarations.
macro_rules! re_export {
($(pub fn $i:ident($($arg:ident: $argty:ty)*) -> $ret:ty;)*) => ($(
extern {
pub fn $i($($arg: $argty),*) -> $ret;
}
)*);
($(pub fn $i:ident($($arg:ident: $argty:ty)*);)*) => ($(
extern {
pub fn $i($($arg: $argty),*);
}
)*);
}
Which is used like this:
re_export! {
pub fn abs(i: c_int) -> c_int;
pub fn rand() -> c_int;
pub fn foo();
pub fn add(i: c_int, j: c_int) -> c_int;
}
How can I generalize the macro so that I can give it multiple functions with or without args and return types and have it work on all of them. It's easy to make a macro that works on several functions of the same type, but I can't figure out how to make it work for different types.
Well, there are two ways.
If you want to parse this exact syntax, then you'll need to use a muncher. So, something like:
macro_rules! re_export {
() => {};
(
pub fn $i:ident($($arg:ident: $argty:ty)*) -> $ret:ty;
$($tail:tt)*
) => {
extern {
pub fn $i($($arg: $argty),*) -> $ret;
}
re_export! { $($tail)* }
};
(
pub fn $i:ident($($arg:ident: $argty:ty)*);
$($tail:tt)*
) => {
extern {
pub fn $i($($arg: $argty),*);
}
re_export! { $($tail)* }
};
}
This involves breaking off one function signature at a time, processing them recursively. This is the most flexible way of parsing things, but does mean that you can run up against the macro recursion limit. The default limit is 64, so if you have more input than that, you'll need multiple top-level macro invocations, or you'll have to manually raise the recursion limit by adding a #![recursion_limit="128"] attribute to your crate.
The other is to change the syntax so that you split then process the signatures in two steps. To do this, you must have some kind of regular top-level syntax for the signatures. For example:
macro_rules! re_export {
($({$($sigs:tt)*})*) => {
$(
re_export! { #fn $($sigs)* }
)*
};
(#fn pub fn $i:ident($($arg:ident: $argty:ty),*) -> $ret:ty) => {
extern {
pub fn $i($($arg: $argty),*) -> $ret;
}
};
(#fn pub fn $i:ident($($arg:ident: $argty:ty),*)) => {
extern {
pub fn $i($($arg: $argty),*);
}
};
}
Here, we wrap each function signature in {...}s. This is because matcher groups ((...), [...], and {...}) allow macro_rules! to match their contents blindly, without having to understand them. This allows us to match the irregular function signatures in a regular fashion. The top-level expansion simply forwards each individual function signature back to itself for actual processing. The #fn is just an internal rule marker to make sure we select the correct rule during recursion.
This doesn't have the same recursion limits that the previous one does... but requires you to use a slightly obtuse syntax:
re_export! {
{ pub fn abs(i: c_int) -> c_int }
{ pub fn rand() -> c_int }
{ pub fn foo() }
{ pub fn add(i: c_int, j: c_int) -> c_int }
}

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).

How to do `Hash` to a unit struct in rust?

I'd like to implement a kind of thing which can be uniquely identified, besides, it contains no other fields. It 's kinds of like BasicObject in ruby, or Object in java.
I have added a PartialEq trait.
struct Thing;
impl PartialEq for Thing {
fn eq(&self, other: &Thing) -> bool {
unsafe {
self as *const Thing == other as *const Thing
}
}
}
Now, I want to allow that it can be hashed, and the question is how could I implement Hash trait for a unit struct which has no fileds ? Or, should I add some kind of object_id to it ?
You cannot rely on pointer address to determine for sure unicity of unit structs in Rust, for example the following code will print two times the same address:
struct Foo;
fn main() {
let x = Foo;
let y = Foo;
println!("{}", &x as *const _);
println!("{}", &y as *const _);
}
You need to have them occupy at least one byte in memory, for example by giving them a field that you never use :
struct Thing {
x: u8;
}
impl Thing {
fn new() -> Thing {
Thing { x: 0u8 }
}
}
Then, a simple way would be to continue on the same track as you are going, and using the address of your objects in memory as a base value for the Hash :
use std::hash::{hash,Hash};
use std::hash::sip::SipState;
struct Thing;
impl Hash for Thing {
fn hash(&self, state: &mut SipState) {
let addr = self as *const Thing as u64;
addr.hash(state);
}
}
fn main() {
let f = Thing;
println!("{}", hash(&f));
}
Note : casting &Thing to *const Thing (and then to u64) is not unsafe, so you don't need the unsafe{} block in your implementation of PartialEq.