How to distinguish different kinds of items in macro_rules macros? - rust

I want to write a macro_rules based macro that will be used to wrap a series of type aliases and struct definitions. I can match on "items" with $e:item, but this will match both aliases and structs. I would like to treat the two separately (I need to add some #[derive(...)] just on the structs). Do I have to imitate their syntax directly by matching on something like type $name:ident = $type:ty; or is there a better way? This route seems annoying for structs because of regular vs tuple like. If I also wanted to distinguish functions that would be really painful because they have a lot of syntactical variation.

I believe for that problem somewhat simple cases can be solved with macro_rules!, but that probably would be limited (you can't lookahead) and super error-prone. I only cover an example for types, I hope that would be convincing enough to avoid macro_rules!. Consider this simple macro:
macro_rules! traverse_types {
($(type $tp:ident = $alias:ident;)*) => {
$(type $tp = $alias;)*
}
}
traverse_types!(
type T = i32;
type Y = Result<i32, u64>;
);
That works fine for the trivial aliases. At some point you probably also would like to handle generic type aliases (i.e. in the form type R<Y> = ...). Ok, your might still rewrite the macro to the recursive form (and that already a non-trivial task) to handle all of cases. Then you figure out that generics can be complex (type-bounds, lifetime parameters, where-clause, default types, etc):
type W<A: Send + Sync> = Option<A>;
type X<A: Iterator<Item = usize>> where A: 'static = Option<A>;
type Y<'a, X, Y: for<'t> Trait<'t>> = Result<&'a X, Y>;
type Z<A, B = u64> = Result<A, B>;
Probably all of these cases still can be handled with a barely readable macro_rules!. Nevertheless it would be really hard to understand (even to the person who wrote it) what's going on. Besides, it is hard to support new syntax (e.g. impl-trait alias type T = impl K), you may even need to have a complete rewrite of the macro. And I only cover the type aliases part, there's more to handle for the structs.
My point is: one better avoid macro_rules! for that (and similar) problem(-s), procedural macros is a way much a better tool for that. It easier to read (and thus extend) and handles new syntax for free (if syn and quote crates are maintained). For the type alias this can be done as simple as:
extern crate proc_macro;
use proc_macro::TokenStream;
use syn::parse::{Parse, ParseStream};
struct TypeAliases {
aliases: Vec<syn::ItemType>,
}
impl Parse for TypeAliases {
fn parse(input: ParseStream) -> syn::Result<Self> {
let mut aliases = vec![];
while !input.is_empty() {
aliases.push(input.parse()?);
}
Ok(Self { aliases })
}
}
#[proc_macro]
pub fn traverse_types(token: TokenStream) -> TokenStream {
let input = syn::parse_macro_input!(token as TypeAliases);
// do smth with input here
// You may remove this binding by implementing a `Deref` or `quote::ToTokens` for `TypeAliases`
let aliases = input.aliases;
let gen = quote::quote! {
#(#aliases)*
};
TokenStream::from(gen)
}
For the struct parsing code is the same using ItemStruct type and also you need a lookahead to determine wether it's an type-alias or a struct, there's very similar example at syn for that.

Related

Is there a way to capture the parameter types of a function definition with a macro?

Ultimately, I'd like to be able to wrap a function in a proc_macro like this:
native_function! {
fn sum(x:i32, y:i32) -> i32 {
x+y
}
}
I'm trying to find what I need to put in this function:
#[proc_macro]
pub fn native_function(item: TokenStream) -> TokenStream {
...
}
Inside the function above, I need to be able to capture the types of the parameters defined in the function; in this case - "i32"
I've tried all sorts of ways to do this, but currently I'm stumped. I'm trying to avoid using a derive macro since I don't want to have to complete and instantiate a struct. I don't even need a full solution, but if someone can point me to the function/object/library I need to be using to that would be great.
syn is the cannonical parsing library for proc macros.
Using it this is easy:
let f = syn::parse_macro_input!(item as syn::ItemFn);
let types: Vec<syn::Type> = f
.sig
.inputs
.into_iter()
.filter_map(|arg| match arg {
syn::FnArg::Receiver(_) => None,
syn::FnArg::Typed(syn::PatType { ty, .. }) => Some(*ty),
})
.collect();
By the way, your macro can be an attribute macro.
If you want to know whethere the type is a some known type, note first that you can never be sure; that is because macros operate without type information, and code like struct i32; is legal and will shadow the primitive i32 type.
If you've accepted this limitation, what you actually want is to compare it to a path type. Path types are represented by the Path variant of syn::Type. It has qself, which is the T in <T as Trait>::AssociatedType and None in a simple path (not fully-qualified, just A::B::C), and path, which is the Trait::AssociatedType or A::B::C. Paths are complex; they can contain generics, for example, but if all you want is to check if this is one-segment type of simple identifier like i32, syn has got you covered: just use Path::is_ident(). So:
let is_i32 = match ty {
syn::Type::Path(ty) => ty.path.is_ident("i32"),
_ => false,
}
If you want to compare against a more complex type, you will have to walk and match segment-by-segment.

Box::new macro like vec! in Rust

I've seen a lot of usage of Box::new in Rust language.
https://doc.rust-lang.org/book/ch15-01-box.html
fn main() {
let b = Box::new(5);
println!("b = {}", b);
}
However, I think this is a bit redundant, especially, in the code like this:
enum List {
Cons(i32, Box<List>),
Nil,
}
use crate::List::{Cons, Nil};
fn main() {
let list = Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil))))));
}
Now, I want to write in a concise way using Macro as we've seen as vec!
https://doc.rust-lang.org/book/ch19-06-macros.html?highlight=vec#declarative-macros-with-macro_rules-for-general-metaprogramming
let v: Vec<u32> = vec![1, 2, 3];
so that with box! macro, we could write:
fn main() {
let b = box!(5);
println!("b = {}", b);
}
or
fn main() {
let list = Cons(1, box!(Cons(2, box!(Cons(3, box!(Nil))))));
}
My question is
Is there any reason that box! macro is not popular even it's
used frequently in Rust?
What is the proper way to define the macro? Is there any points of attention?
This is a bit subjective, but vectors are used far more often, I believe. And the vec! macro saves you a lot more code compared to the box! macro you are referring to.
As for the macro implementation, you can write something like this:
macro_rules! bx {
($e:expr) => {
Box::new($e)
};
}
Here is an example usage:
let b = Box::new(3);
let m = bx!(7);
Note that I named it bx! and not box!, as the latter is a reserved word in Rust.
however, for some unknown reason Rust guys prefer to use macro for vec! or println!
I suggest asking this the other way around: why couldn't vec! and println! be functions? And the answer is, both are variadic (can take variable number of arguments) and there are no variadic functions in Rust. It's much worse for println!, whose arguments can have different types too; and constraints on those types depend on the first argument! Try writing down a type for println as a function, and you'll find it would be much less usable and less efficient.
The problem vec! solves is smaller. But as Chayim Friedman's comment says, until quite recently you couldn't even have had
Vec::from([1,2,3])
if there were no vec! macro. I think the best you could have done was
Vec::from(&[1,2,3])
As Chayim points out, this is strictly worse than vec!.
Of course, Box::new doesn't have this problem at all. So it's a function just because it didn't have to be a macro.
but as I post the Cons list expample, it's obvious the latter syntax is redundant and does have an issue for readability
In that case box! is no less redundant; instead a way to get rid of redundancy is
fn cons(head: i32, tail: List) -> List {
List::Cons(head, Box::new(tail))
}
and then
let list = cons(1, cons(2, cons(3, Nil)));
Or even a list! macro and
let list = list![1,2,3];
Thanks to #at54321, I googled and found
https://doc.rust-lang.org/beta/unstable-book/language-features/box-syntax.html
#![feature(box_syntax)]
fn main() {
let b = box 5;
}
So, already there.

How to give multiple function parameters the same type?

When declaring a function in Go, one can give multiple parameters the same type:
func test(a, b, c uint8) { }
Does Rust have a way to give multiple parameters the same type without explicitly specifying each one of them manually?
This doesn't seem to work:
fn test(a, b, c: u8) { }
Simply:
fn test(a: u8, b: u8, c: u8) {}
There is no short-cut syntax available if you want to name each individually.
If you do not care for individual names:
fn test(a: &[u8; 3]) {}
And if the number of items ought to be dynamic:
fn test(a: &[u8]) {}
I would note that, personally, I generally find the idea of passing multiple parameters of the same type to a function a rather brittle design in the absence of named parameters.
Unless those parameters are interchangeable, in which case swapping them doesn't matter, then I recommend avoiding such function signatures. And by extension, I do not see the need for a special syntax to accommodate the brittleness.
In exchange, Rust features tuple structs: struct X(u8);, allowing one to quickly whip up new types to represent new concepts, rather than fall into Primitive Obsession.

How do I solve the error "the precise format of `Fn`-family traits' type parameters is subject to change"?

I have written a problem solver in Rust which as a subroutine needs to make calls to a function which is given as a black box (essentially I would like to give an argument of type Fn(f64) -> f64).
Essentially I have a function defined as fn solve<F>(f: F) where F : Fn(f64) -> f64 { ... } which means that I can call solve like this:
solve(|x| x);
What I would like to do is to pass a more complex function to the solver, i.e. a function which depends on multiple parameters etc.
I would like to be able to pass a struct with a suitable trait implementation to the solver. I tried the following:
struct Test;
impl Fn<(f64,)> for Test {}
This yield the following error:
error: the precise format of `Fn`-family traits' type parameters is subject to change. Use parenthetical notation (Fn(Foo, Bar) -> Baz) instead (see issue #29625)
I would also like to add a trait which includes the Fn trait (which I don't know how to define, unfortunately). Is that possible as well?
Edit:
Just to clarify: I have been developing in C++ for quite a while, the C++ solution would be to overload the operator()(args). In that case I could use a struct or class like a function. I would like to be able to
Pass both functions and structs to the solver as arguments.
Have an easy way to call the functions. Calling obj.method(args) is more complicated than obj(args) (in C++). But it seems that this behavior is not achievable currently.
The direct answer is to do exactly as the error message says:
Use parenthetical notation instead
That is, instead of Fn<(A, B)>, use Fn(A, B)
The real problem is that you are not allowed to implement the Fn* family of traits yourself in stable Rust.
The real question you are asking is harder to be sure of because you haven't provided a MCVE, so we are reduced to guessing. I'd say you should flip it around the other way; create a new trait, implement it for closures and your type:
trait Solve {
type Output;
fn solve(&mut self) -> Self::Output;
}
impl<F, T> Solve for F
where
F: FnMut() -> T,
{
type Output = T;
fn solve(&mut self) -> Self::Output {
(self)()
}
}
struct Test;
impl Solve for Test {
// interesting things
}
fn main() {}

How to write a fn that processes input and returns an iterator instead of the full result?

Forgive me if this is a dumb question, but I'm new to Rust, and having a hard time writing this toy program to test my understanding.
I want a function that given a string, returns the first word in each line, as an iterator (because the input could be huge, I don't want to buffer the result as an array). Here's the program I wrote which collects the result as an array first:
fn get_first_words(input: ~str) -> ~[&str] {
return input.lines_any().filter_map(|x| x.split_str(" ").nth(0)).collect();
}
fn main() {
let s = ~"Hello World\nFoo Bar";
let words = get_words(s);
for word in words.iter() {
println!("{}", word);
}
}
Result (as expected):
Hello
Foo
How do I modify this to return an Iterator instead? I'm apparently not allowed to make Iterator<&str> the return type. If I try #Iterator<&str>, rustc says
error: The managed box syntax is being replaced by the `std::gc::Gc` and `std::rc::Rc` types. Equivalent functionality to managed trait objects will be implemented but is currently missing.
I can't figure out for the life of me how to make that work.
Similarly, trying to return ~Iterator<&str> makes rustc complain that the actual type is std::iter::FilterMap<....blah...>.
In C# this is really easy, as you simply return the result of the equivalent map call as an IEnumerable<string>. Then the callee doesn't have to know what the actual type is that's returned, it only uses methods available in the IEnumerable interface.
Is there nothing like returning an interface in Rust??
(I'm using Rust 0.10)
I believe that the equivalent of the C# example would be returning ~Iterator<&str>. This can be done, but must be written explicitly: rather than returning x, return ~x as ~Iterator<&'a str>. (By the way, your function is going to have to take &'a str rather than ~str—if you don’t know why, ask and I’ll explain.)
This is not, however, idiomatic Rust because it is needlessly inefficient. The idiomatic Rust is to list the return type explicitly. You can specify it in one place like this if you like:
use std::iter::{FilterMap, Map};
use std::str::CharSplits;
type Foo = FilterMap<'a, &'a str, &'a str,
Map<'a, &'a str, &'a str,
CharSplits<'a, char>>>
And then list Foo as the return type.
Yes, this is cumbersome. At present, there is no such thing as inferring a return type in any way. This has, however, been discussed and I believe it likely that it will come eventually in some syntax similar to fn foo<'a>(&'a str) -> Iterator<&'a str>. For now, though, there is no fancy sugar.

Resources