I mean, see code
macro_rules! impls {
() => {
let a = "Hello, world!";
println!("{}", match 7 {
impls!{ 3 + 4, a }
_ => unimplemented!()
})
};
($x:expr, $name:ident) => {
($x) => $name,
}
}
fn main() {
impls!{ }
}
As I think, impls!{ 3 + 4, a } should be (3 + 4) => a, but it's not. What's wrong?
Compiler error:
error: expected one of `=>`, `if`, or `|`, found reserved identifier `_`
--> src/main.rs:6:13
|
5 | impls!{ 3 + 4, a }
| - expected one of `=>`, `if`, or `|`
6 | _ => unimplemented!()
| ^ unexpected token
...
16 | impls!{ }
| --------- in this macro invocation
|
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
That is not possible with declarative macros (it might be with procedural macros, I've very little experience with those so I do not know): per the documentation
Macros can expand to expressions, statements, items (including traits, impls, and foreign items), types, or patterns.
A match arm is not in and of itself a "Rust construct", it's a sub-item of a match expression (composed of a pattern, an optional guard expression and a body expression).
As a result, it can not be produced by a declarative macro.
While macros cannot expand to match arm, as said, it is still possible to use them for patterns:(playground)
struct A(usize);
struct B(f32);
macro_rules! impls {
() => {
let x = A(3 + 4);
println!("{}", match x {
impls!{ A -> 7 } => "Hello,",
_ => unimplemented!(),
})
};
($e:expr) => {
let x = B(4.0);
println!("{}", match x {
impls!{ B -> x } => if x==$e { "world!" } else {"None"},
})
};
($a:tt -> $x:tt) => { $a($x) };
}
fn main() {
impls!{ }
impls!{ 16f32.sqrt() }
impls!{ }
impls!{ 1.0 }
}
Related
I am using proc macros to parse a given input into a Node tree, for debugging purposes I want to stringify and print the output to see if I am successfully converting to RPN, my current function:
use proc_macro::*;
#[proc_macro]
pub fn symbolic(body: TokenStream) -> TokenStream {
// ---shunting yard---
let mut stack : Vec<TokenTree> = vec![];
let mut que : Vec<TokenTree> = vec![];
for tt in body.into_iter(){
match tt {
TokenTree::Ident(_) => que.push(tt),
TokenTree::Punct(_) => {
while precedence(Some(&tt)) <= precedence(stack.last()){
que.push(stack.pop().unwrap());
}
stack.push(tt)
},
TokenTree::Literal(_) => que.push(tt),
_ => {}
}
}
while let Some(op) = stack.pop() {
que.push(op);
}
println!(stringify!(output_que));
"fn answer() -> u32 { 42 }".parse().unwrap()
}
fn precedence(tt: Option<&TokenTree>) -> usize{
if let Some(TokenTree::Punct(punct)) = tt{
match punct.as_char() {
'^' => 3,
'/' | '*' => 2,
'-' | '+' => 1,
_ => 0
}
} else {
0
}
}
gives me an error
error: proc macro panicked
--> src\main.rs:5:5
|
5 | symbolic!(x^2 + 2*x)
| ^^^^^^^^^^^^^^^^^^^^
|
= help: message: called `Option::unwrap()` on a `None` value
which I do not understand as this should be outputting an empty token stream since TokenStream::new() -> TokenStream { TokenStream(None) } is this not acceptable, if not I do not understand why. I changed this to the example given in the rust book "fn answer() -> u32 { 42 }".parse().unwrap() and still the same error so I don't know what?
Your macro doesn't work because you never push anything to stack
but here:
while precedence(&tt) <= precedence(stack.last().unwrap()){
que.push(stack.pop().unwrap());
You unwrap as if stack.pop or stack.last was guaranteed to return Some.
I have an enum:
enum DaEnum{
One(u8),
Strang(String),
}
I would like to variable y to be assigned value from enum:
let x = DaEnum::One(1);
let y;
match x{
DaEnum::One(one) => {y = one},
DaEnum::Strang(strang) => {y = strang},
}
println!("Value of y =>{:?}",y);
Here is the error from cargo run:
error[E0308]: mismatched types
--> src\main.rs:33:40
|
30 | let y;
| - expected due to the type of this binding
...
33 | DaEnum::Strang(strang) => {y = strang},
| ^^^^^^ expected `u8`, found struct `String`
Desired case would be when x is 1, y is 1and when x is a String, y would also be a String.
A generic function may help with your case if it's simple enough. You can pick up all the common behavior into one function and invoke it in every match arm. Please be aware of the trait because it must be shared by all your usages.
#[derive(Clone)]
enum DaEnum {
One(u8),
Strang(String),
}
fn main() {
let x = DaEnum::One(1);
fn common_stuff<T: std::fmt::Debug>(v: T) {
// common stuff
println!("Value of y =>{:?}", v);
}
// (1)
match x.clone() {
DaEnum::One(one) => {
let y = one;
// not common stuff
common_stuff(y);
}
DaEnum::Strang(strang) => {
let y = strang;
// not common stuff
common_stuff(y);
}
}
// (2)
match x {
DaEnum::One(one) => common_stuff(one),
DaEnum::Strang(strang) => common_stuff(strang),
}
}
Given the macro matching example, this shows how macros can match an argument.
I've made very minor changes here to use numbers:
macro_rules! foo {
(0 => $e:expr) => (println!("mode X: {}", $e));
(1 => $e:expr) => (println!("mode Y: {}", $e));
}
fn main() {
foo!(1 => 3);
}
Works, printing: mode Y: 3
However I would like to use a constant as an argument, can this be made to work:
const CONST: usize = 1;
macro_rules! foo {
(0 => $e:expr) => (println!("mode X: {}", $e));
(1 => $e:expr) => (println!("mode Y: {}", $e));
}
fn main() {
foo!(CONST => 3);
}
Is this possible in Rust?
Note, using a regular match statement isn't usable for me, since in my code each branch resolves to different types, giving an error.
So I'm specifically interested to know if a constant can be passed to a macro.
No.
Macros operate on the Abstract Syntax Tree, so they reason at the syntactic level: they reason about tokens and their spelling.
For example:
fn main() {
let v = 3;
}
In this case, the AST will look something like:
fn main
\_ let-binding v
\_ literal 3
If you ask a macro whether v is 3, it will look at you funny, and wonder why you would try comparing a variable name and a literal.
I'm fairly sure the answer is "no"; at macro expansion time all you have are token trees - expansion happens before evaluation, or even type inference/checking.
const CONST: usize = 0;
macro_rules! foo {
($i:ident => $e:expr) => {
if $i == 0 {
println!("mode X: {}", $e);
} else if $i == 1 {
println!("mode Y: {}", $e);
}
};
}
fn main() {
foo!(CONST => 3);
}
If you want use identifier in macro it needs to be ident tag and you can use if, else if blocks instead of match.
In a macro_rules! transcriber, nested repetitions are not handled properly. This works fine:
macro_rules! demo_macro {
($op:tt; $($arg:tt),*) {
($($op * $arg),*)
}
}
fn main() {
println!("{?:}", demo_macro!(2; 1,2,3));
}
and outputs (2, 4, 6), but this does not compile:
macro_rules! demo_macro {
([$($op:tt)*] $($arg:tt),*) {
($($($op)* $arg),*)
}
}
fn main() {
println!("{?:}", demo_macro!([2*] 1,2,3));
}
and results in this error message:
error: meta-variable `op` repeats 2 times, but `arg` repeats 3 times
--> src/main.rs:3:11
|
3 | ($($($op)* $arg),*)
| ^^^^^^^^^^^^^^
If I change 1,2,3 to 1,2 so the repetition counts match up, I get this error message:
error: attempted to repeat an expression containing no syntax variables matched as repeating at this depth
--> src/main.rs:3:13
|
3 | ($($($op)* $arg),*)
| ^^^^^
Any idea how to make demo_macro!([2*] 1,2,3) result in (2, 4, 6)?
Yay, this works!
macro_rules! unbracket {
(_ [$($tt1:tt)*]) => { $($tt1)* };
(() [$($tt1:tt)*]) => { ($($tt1)*) };
([] [$($tt1:tt)*]) => { [$($tt1)*] };
({} [$($tt1:tt)*]) => { {$($tt1)*} };
($tt0:tt [$($tt1:tt)*] #unbracket ($($tt2:tt)*) $($tt3:tt)*) => { unbracket!{ $tt0 [$($tt1)* $($tt2)*] $($tt3)*} };
($tt0:tt [$($tt1:tt)*] #unbracket [$($tt2:tt)*] $($tt3:tt)*) => { unbracket!{ $tt0 [$($tt1)* $($tt2)*] $($tt3)*} };
($tt0:tt [$($tt1:tt)*] #unbracket {$($tt2:tt)*} $($tt3:tt)*) => { unbracket!{ $tt0 [$($tt1)* $($tt2)*] $($tt3)*} };
($tt0:tt [$($tt1:tt)*] $tt2:tt $($tt3:tt)*) => { unbracket!{$tt0 [$($tt1)* $tt2] $($tt3)*} };
}
macro_rules! demo_macro {
($op:tt $($arg:tt),*) => {
unbracket!(() [] $(#unbracket $op $arg),*)
}
}
fn main() {
println!("{:?}", demo_macro!([2*] 1,2,3));
}
Clippy warns about code like this:
fn func<T>(data: &Option<T>) {
if let &Some(ref value) = data {}
}
warning: you don't need to add `&` to all patterns
--> src/main.rs:2:5
|
2 | if let &Some(ref value) = data {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: #[warn(match_ref_pats)] on by default
= help: for further information visit https://rust-lang-nursery.github.io/rust-clippy/v0.0.210/index.html#match_ref_pats
help: instead of prefixing all patterns with `&`, you can dereference the expression
|
2 | if let Some(ref value) = *data {}
| ^^^^^^^^^^^^^^^ ^^^^^
Are these constructions the same from compiler point of view:
if let &Some(ref value) = data {
if let Some(ref value) = *data {
If so, what's the point in the Clippy message, just to use uniform style?
Yes, these are the same to the compiler. In this case, there's not much benefit. The real benefit comes from the match equivalent:
fn func(data: &Foo) {
match data {
&Foo::One => {}
&Foo::Two => {}
&Foo::Three => {}
}
}
Here, you only have to place a single dereference, not 3 references in the patterns:
fn func(data: &Foo) {
match *data {
Foo::One => {}
Foo::Two => {}
Foo::Three => {}
}
}
And since Rust 1.26, you don't even have to dereference the expression being matched on:
fn func(data: &Foo) {
match data {
Foo::One => {}
Foo::Two => {}
Foo::Three => {}
}
}
That's why it's the idiomatic choice.
The if let concept is just an extension from this.
You can't always dereference the value. If you tried to do the same thing for a pair of items:
fn func(data: &Foo, data2: &Foo) {
match (*data, *data2) {
(Foo::One, _) => {}
(Foo::Two, _) => {}
(Foo::Three, _) => {}
}
}
You get the error
error[E0507]: cannot move out of borrowed content
--> src/main.rs:8:12
|
8 | match (*data, *data2) {
| ^^^^^ cannot move out of borrowed content
In this case, you can use the reference form:
fn func(data: &Foo, data2: &Foo) {
match (data, data2) {
(&Foo::One, _) => {}
(&Foo::Two, _) => {}
(&Foo::Three, _) => {}
}
}
Or, since Rust 1.26, perform some implicit references:
fn func(data: &Foo, data2: &Foo) {
match (data, data2) {
(Foo::One, x) => {}
(Foo::Two, _) => {}
(Foo::Three, _) => {}
}
}