Compilation error when passing a argument with :: to a macro invocation - rust

I was attempting a macro like following
from_converter!(std::io::Error, MyError);
macro_rules! from_converter {
($e: tt, $n: tt) => {
impl std::convert::From<$e> for Error {
fn from(source: $e) -> Self {
$n.into_error(source)
}
}
};
}
The compilation error I get is:
no rules expected the token `::`
no rules expected this token in macro callrustc
error.rs(37, 1): when calling this macro
error.rs(47, 20): no rules expected this token in macro call

tt is a TokenTree, which means a singular token. The first one ($e) should be a type, denoted as ty, and the second ($n) should be an expression, denoted as expr:
macro_rules! from_converter {
($e: ty, $n: expr) => { /* ... */ };
}

Related

Why does tt in declarative macros not expect the token "::"?

I have written the following macro:
macro_rules! foo {
($bar:tt) => {
fn baz() {
$tt
}
}
}
I am using it like this:
foo! {
String::new();
}
This produces the following error: error: no rules expected the token :: label: no rules expected this token in the macro call
I find this very confusing since I was under the impression that tt can match any regular code token.
tt matches a single token tree, i.e. only the String. If you want to match any sequence of tokens, use repeated tt:
macro_rules! foo {
($($bar:tt)*) => {
fn baz() {
$($bar)*
}
}
}

Simplifying a `match` using a Rust macro

There are many question functions (hundreds), and each may have a different type. For each question I want to run a run_question function, which shows how long that function took, and print it's output.
I'm trying to shorten the following match expression with a Rust macro (writing run_question 100s of times does make the code rather long):
fn run_question<T: std::fmt::Display>(question_func: fn() -> T) {
let begin = Instant::now();
let output: T = question_func();
let elapsed_secs = begin.elapsed().as_micros() as f32 / 1e6;
println!("{}", output);
println!("{:.6}s taken", elapsed_secs);
}
fn q1() -> u8 { /* ... */ }
fn q2() -> u32 { /* ... */ }
fn q3() -> u64 { /* ... */ }
fn q4() -> String { /* ... */ }
fn main() {
// ...
match question_num {
1 => run_question(q1), 2 => run_question(q2), 3 => run_question(q3), 4 => run_question(q4),
_ => {
println!("Question doesn't exist.");
},
}
}
I have no experience in writing macros, and attempted the following which doesn't exactly work. It gives the error:
error: variable 'question_num' is still repeating at this depth
I'm rather stumped too how I can print the Question doesn't exist. as a default case.
#[macro_export]
macro_rules! run_questions {
( $chosen_question: expr, $( $question_num: expr, $question_mod: expr ), * ) => {
{
if $chosen_question == $question_num {
run_question($question_mod::solve);
}
}
};
}
The way I'd like to use it, is (or anything just as short is fine as well):
run_questions!(question_num, 1, q1, 2, q2, 3, q3, 4, q4);
I read a bit of the Rust book, but there aren't exactly that many examples of macros.
How would I go about doing this?
Rather than many if statements, I just reproduced the match statement
with a repetition $( ... )* for all the available branches.
It seems to behave like the extensive match expression.
macro_rules! run_questions {
( $chosen_question: expr, $( $question_num: expr, $question_mod: expr ), * ) => {
match $chosen_question {
$($question_num => run_question($question_mod),)*
_ => {
println!("Question doesn't exist.");
}
}
};
}
The error message explained:
macro_rules! run_questions {
($chosen_question: expr, $($question_num: expr, $question_mod: expr),*) => {{
In the above pattern you have a repetition with the * operator that involves variables $question_num and $question_mod
if $chosen_question == $question_num {
run_question($question_mod::solve);
}
In the corresponding code, you can't use $question_num and $question_mod directly: since they are repeated they potentially have more than one value and which one should the compiler use here? Instead, you need to tell the compiler to repeat the block of code that uses these variables. This is done by surrounding the repeated code block with $() and adding the * operator:
$(if $chosen_question == $question_num {
run_question($question_mod::solve);
})*
Although as pointed out by #prog-fh's answer, better to use a match in the macro, same as in the straight code:
match $chosen_question {
$($question_num => run_question ($question_mod::solve),)*
_ => println!("Question doesn't exist.")
};

Rust macro error: local ambiguity: multiple parsing options

The following rust code does not compile because of the macro error
error: local ambiguity: multiple parsing options: built-in NTs stmt ('s') or 1 other option.
macro A is fine. Macro B shows the error.
macro_rules! A {
($x: ident, $($s: stmt)*) => {
println!("hello");
};
}
macro_rules! B {
($x: ident, $($s: stmt)*; $e: expr) => {
println!("hello");
};
}
fn main() {
A![my_name, let x=5];
B![my_name, let x=5; 5];
}
This minimal reproducible example in B is exactly what I need. I want the macro to accept multiple let statements and terminate by some other expression.
What is the ambiguity that is being referred to?
Is there a way around it?
Of those tokens accepted after statement fragments I have tried several combinations yet none appear to make a difference. Neither does swapping the statement with a token tree.
Expressions are statements, so $($s: stmt)*; $e: expr is ambiguous because the compiler can't decide between using s or e when it encounters one.
Since you only expect bindings, you can easily expand them yourself:
macro_rules! A {
($x: ident, $($s: stmt)*) => {
println!("hello");
};
}
macro_rules! B {
($x: ident, $(let $p:pat = $v:expr)*; $e: expr) => {
$(let $p = $v);*
println!("hello: {}", $e);
};
}
fn main() {
A![my_name, let x=5];
B![my_name, let x=5; x+2];
}
Note that this doesn't support including types in the binding (let a: i32 = 42;) because pat can't be followed by :.

Why do I get "unexpected token" when trying to call an inner macro with the argument of an outer macro?

I don't understand this failure while trying to pass the expression received by the higher! macro to the lower! macro:
// A low-level macro using only Rust primitives.
macro_rules! lower {
(x, $a:expr) => {
println!("x is {}", $a);
};
(x($b:expr), $a:expr) => {
println!("x({}) is rather {}", $b, $a);
};
}
// A higher-level macro using my own previous macro.
macro_rules! higher {
($xstuff:expr, $a:expr) => {
// Here, I expect transferring the expression $xstuff to lower!.. but it fails.
lower!($xstuff, $a)
};
}
fn main() {
lower!(x, '5'); // x is 5
lower!(x(8), '6'); // x(8) is rather 6
higher!(x(7), '9');
}
error: no rules expected the token `x(7)`
--> src/main.rs:15:16
|
2 | macro_rules! lower {
| ------------------ when calling this macro
...
15 | lower!($xstuff, $a)
| ^^^^^^^ no rules expected this token in macro call
...
23 | higher!(x(7), '9');
| ------------------- in this macro invocation
I would expect this last token to be expected by the rule in lower!, but the compiler tells me it is unexpected. What am I missing here? How can I transfer the expression received by higher! as $xstuff to lower!?
After the call to higher!, x(7) has been parsed as a complete expression held by the macro variable $xstuff:
($xstuff:expr, $a:expr) => { /* ... */ }
// ^~~~
However, neither of the macro rules for lower! accept an arbitrary expression as the first argument, they only accept the token x:
(x, $a:expr) => { /* ... */ }
// ^
(x($b:expr), $a:expr) => { /* ... */ }
// ^
The easiest fix is to place the same restrictions about the x in the higher macro:
macro_rules! higher {
(x($xstuff:expr), $a:expr) => {
lower!(x($xstuff), $a)
};
}
An alternate solution (that changes the calling syntax) is to not immediately parse x(7) as an expression, but instead a collection of token trees. You need to add additional grouping at the call site so that the parser knows when to stop though:
macro_rules! higher {
(($($xstuff:tt)*), $a:expr) => {
lower!($($xstuff)*, $a)
};
}
fn main() {
higher!((x(7)), '9');
}
See also:
The Little Book of Rust Macros
Captures and Expansion Redux

How do I assert an enum is a specific variant if I don't care about its fields?

I'd like to check enums with fields in tests while ignoring the actual value of the fields for now.
Consider the following example:
enum MyEnum {
WithoutFields,
WithFields { field: String },
}
fn return_with_fields() -> MyEnum {
MyEnum::WithFields {
field: "some string".into(),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn example() {
assert_eq!(return_with_fields(), MyEnum::WithFields {..});
}
}
playground
I'd like to use assert_eq! here, but the compiler tells me:
error: expected expression, found `}`
--> src/lib.rs:18:64
|
18 | assert_eq!(return_with_fields(), MyEnum::WithFields {..});
| ^ expected expression
This is similar to Why do I get an error when pattern matching a struct-like enum variant with fields?, but the solution does not apply in my case.
Of course, I can use match and do it myself, but being able to use assert_eq! would be less work.
Rust 1.42
You can use std::matches:
assert!(matches!(return_with_fields(), MyEnum::WithFields { .. }));
Previous versions
Your original code can be made to work with a new macro:
macro_rules! is_enum_variant {
($v:expr, $p:pat) => (
if let $p = $v { true } else { false }
);
}
#[test]
fn example() {
assert!(is_enum_variant!(return_with_fields(), MyEnum::WithoutFields {..}));
}
Personally, I tend to add methods to my enums:
fn is_with_fields(&self) -> bool {
match self {
MyEnum::WithFields { .. } => true,
_ => false,
}
}
I also tend to avoid struct-like enums and instead put in extra work:
enum MyEnum {
WithoutFields,
WithFields(WithFields),
}
struct WithFields { field: String }
impl MyEnum {
fn is_with_fields(&self) -> bool {
match self {
MyEnum::WithFields(_) => true,
_ => false,
}
}
fn as_with_fields(&self) -> Option<&WithFields> {
match self {
MyEnum::WithFields(x) => Some(x),
_ => None,
}
}
fn into_with_fields(self) -> Option<WithFields> {
match self {
MyEnum::WithFields(x) => Some(x),
_ => None,
}
}
}
I hope that some day, enum variants can be made into their own type to avoid this extra struct.
If you are using Rust 1.42 and later, see Shepmaster's answer below.
A simple solution here would be to do the opposite assertion:
assert!(return_with_fields() != MyEnum::WithoutFields);
or even more simply:
assert_ne!(return_with_fields(), MyEnum::WithoutFields);
Of course if you have more members in your enum, you'll have to add more asserts to cover all possible cases.
Alternatively, and this what OP probably had in mind, since assert! just panics in case of failure, the test can use pattern matching and call panic! directly in case something is wrong:
match return_with_fields() {
MyEnum::WithFields {..} => {},
MyEnum::WithoutFields => panic!("expected WithFields, got WithoutFields"),
}
I'd use a macro like #Shepmaster proposed, but with more error reporting (like the existing assert! and assert_eq! macros:
macro_rules! assert_variant {
($value:expr, $pattern:pat) => ({
let value = &$value;
if let $pattern = value {} else {
panic!(r#"assertion failed (value doesn't match pattern):
value: `{:?}`,
pattern: `{}`"#, value, stringify!($pattern))
}
})
// TODO: Additional patterns for trailing args, like assert and assert_eq
}
Rust playground demonstrating this example

Resources