In writing Rust code, I frequently want to create my error type in an error block:
match something() {
Ok(x) => {...},
Err(e) => Err(MyError::new(format!("Something failed: {}", e)))
}
There is a lot of repeated code in the Err creation that I would like to extract to a function/macro. Part of it is easy:
fn err(msg: String) -> Result<_, MyError> {
Err(MyError::new(msg))
}
However, there is still boilerplate in the error block:
Err(e) => err(format!("Something failed: {}", e))
Since I also want to be able to do things like
Err(e) => err(format!("Unable to convert {}: {}", input, e)
I can't write a single function to get rid of the format!. So clearly I need a macro. I currently have
#[macro_export]
macro_rules! foo {
($base: expr, $(arg:expr),*) => {{
Err(MyError::new(format!(base, arg)))
}};
}
I don't get errors with the macro definition, but trying to use it gives me compilation errors:
|
231 | macro_rules! foo {
| ---------------- when calling this macro
...
245 | let xxx = foo!("a", "b");
| ^^^ no rules expected this token in macro call
The examples that I find online deal with processing the arguments one at a time, such as bar![1,2,3] to push each value onto a vector. I can't figure out what I need to do to get the macro to match and then pass all the arguments to another macro.
You may be interested in the anyhow crate which contains an anyhow! macro doing something similar.
Your macro is almost good, you're just missing an extra $ in the declaration, and $ prefixes at the usage site.
#[macro_export]
macro_rules! foo {
($base: expr, $($args:tt),*) => {{
Err(MyError::new(format!($base, $($args),*)))
}};
}
Note that you can also pass the whole thing to format! without having a separate base parameter.
#[macro_export]
macro_rules! foo {
($($args:tt),*) => {{
Err(MyError::new(format!($($args),*)))
}};
}
If you wanted to prefix the error message with another string, format_args! can be used instead of format! to save a memory allocation.
#[macro_export]
macro_rules! foo {
($($args:tt),*) => {{
Err(MyError::new(format!("Something failed: {}", format_args!($($args),*))))
}};
}
Related
I'm trying to write a macro to switch between rayon's par_iter and std's iter depending on a build feature (possibly getting ahead of myself, as I've not read a lot on macros yet). A macro seems a bit better to deal with than a function here, since a function might need some relatively complex types to make this work; as well a macro might remain more flexible in the future if I wanted to add even more variations in build features pertaining to how to run iterators.
#[macro_export]
macro_rules! par_iter {
($($tokens:tt)*) => {
#[cfg(feature = "threaded")]
$($tokens)*.par_iter()
#[cfg(not(feature = "threaded"))]
$($tokens)*.iter()
}
}
I see the following error:
error: macro expansion ignores token `b_slice` and any following
--> src/util.rs:28:8
|
28 | $($tokens)*.iter();
| ^^^^^^^^^
|
::: src/counting.rs:219:9
|
219 | par_iter!(b_slice).map(WordCount::from)
| ------------------- help: you might be missing a semicolon here: `;`
| |
| caused by the macro expansion here
|
= note: the usage of `par_iter!` is likely invalid in expression context
While I have no idea about the first error, I'm curious why a ; is expected - how do I make it valid in expression context?
This essentially boils down to, that you aren't allowed to have attributes within expressions like that, e.g. that the following is invalid:
b_slice.iter()
#[cfg(not(feature = "threaded"))]
.map(|x| x)
.collect();
To fix the issue, you can assign them to a temporary variable, like this:
Note the double {{ and }}, which results in a block, such that the final expression is the value that block results in.
#[macro_export]
macro_rules! par_iter {
($($tokens:tt)*) => {{
#[cfg(feature = "threaded")]
let it = $($tokens)*.par_iter();
#[cfg(not(feature = "threaded"))]
let it = $($tokens)*.iter();
it
}};
}
Alternatively, you can also split it into two macros like this:
#[cfg(feature = "threaded")]
#[macro_export]
macro_rules! par_iter {
($($tokens:tt)*) => {
$($tokens)*.par_iter()
}
}
#[cfg(not(feature = "threaded"))]
#[macro_export]
macro_rules! par_iter {
($($tokens:tt)*) => {
$($tokens)*.iter()
}
}
I've found several useful macros in Rust, namely: file!(), line!(), stringify!() I've also found that Rust allows macros with variable arguments, as stated here:
macro_rules! print_all {
($($args:expr),*) => {{
$(
println!("{}", $args);
)*
}}
}
My goal is to somehow combine all those macros inside one which I will use during troubleshooting/debugging. So calling trace! macro on the following example:
let a: i32 = 1;
let b: i32 = 2;
trace!(a,b)
should expand to something like this:
println!("TRACE: file: {}, line: {}, a: {}, b: {}", file!(), line!(), a, b);
Is it possible? If yes, how would such a macro work?
You could do something like this:
macro_rules! trace {
($($args: expr),*) => {
print!("TRACE: file: {}, line: {}", file!(), line!());
$(
print!(", {}: {}", stringify!($args), $args);
)*
println!(""); // to get a new line at the end
}
}
There is likely a small overhead to calling print! multiple times, since each call will result in a system call and will also check for IO errors. However, constructing a single formatting string for arbitrary arguments would need a procedural macro, which I think is beyond the scope of the question.
You could also use a BufWriter to limit it to a single system call, but it might not be worth the effort.
I am new to rust. I am trying to create macro which takes a buffer and then decodes some data out of it and creates givens list of variables. if error occurs then it should print error and continue since I'm gonna call it in a loop where I receive buffers. something like this:-
for bin_ref in bufs {
extract!( bin_ref anime &str episodes u32 season u32);
//if everything goes ok then do some cool stuff with
//above variables otherwise take next buf_ref
}
How can I do this? So for I came with this aproach:-
#[macro_export]
macro_rules! extract {
( $buf:ident $($var:ident $typ:ty),* ) => {
$(
ext_type!( $buf $var $typ );
)*
};
}
#[macro_export]
macro_rules! ext_type {
( $buf:ident $var:ident &str ) => {
let mut $var : &str = ""; //some string specific function
println!("doing cool things with '{}' which is string ",$var);
};
( $buf:ident $var:ident u32 ) => {
let mut $var : u32 = 34; //some u32 specific function
println!("doing cool things with '{}' which is u32",$var);
}
}
I have following test function:-
fn macro_test() {
let mut bin_ref : &[u8] = &[0u8;100];
ext_type!(bin_ref anime &str); // works
ext_type!(bin_ref episodes u32 ); // works
extract!( bin_ref username &str, password &str ); // does not work. why ??
}
When I compile this,I get following error:-
error: no rules expected the token `&str`
--> src/easycode.rs:11:34
|
11 | ext_type!( $buf $var $typ );
| ^^^^ no rules expected this token in macro call
...
19 | macro_rules! ext_type {
| --------------------- when calling this macro
...
48 | extract!( bin_ref username &str, password &str );
| ------------------------------------------------- in this macro invocation
Why I cant just pass $typ to ext_type! macro? it works when called from code
The ext_type! macro's rules require the literal tokens &str and u32 at the end. These literal tokens cannot match the matched fragment $typ:ty in extract!. In order to successfully match the literal tokens to a matched fragment, it must be a tt, ident or lifetime.
The only option that will work in this case is tt, which simply put, is just a parser token. A type is often composed of more than one token though; case in point &str, which consists of two tokens & and str. We must thus use a repetition to fully capture a type with tts: $($typ:tt)+ will do nicely.
Using an unbounded repetition with tt comes at a cost, however -- a tt will match almost everything, so simply substituting $typ:ty with $($typ:tt)+ will not work, as the $typ repetition will capture everything till the end of the macro invocation! To prevent this from happening, we must delimit the type token tree in the macro rule matcher to stop it from consuming everything. At the cost of making invocation slightly verbose, wrapping the repetition in parentheses in will serve us well and stop the token tree matching exactly where we want it to. The modified macro looks like this:
#[macro_export]
macro_rules! extract {
( $buf:ident $($var:ident ($($typ:tt)+)),* ) => {
$(
ext_type!( $buf $var $($typ)+);
)*
};
}
Note the replacement of $typ:ty with ($($typ:tt)+) (which is a token tree repetition wrapped in parentheses) in the matcher, and the replacement of $typ with $($typ)+ in the transcriber.
The macro rule is invoked as follows:
extract!(bin_ref username (&str), password (&str), id (u32));
Rust Playground
So here I am, trucking along with Rustlings, until I get broadsided with test 4.
It wants me to write a macro that will satisfy the following code:
fn main() {
if my_macro!("world!") != "Hello world!" {
panic!("Oh no! Wrong output!");
}
}
So, I wrote this:
macro_rules! my_macro {
($val:expr) => {
println!("Hello {}", $val);
}
}
And Rustlings spat this out:
error[E0308]: mismatched types
--> exercises/test4.rs:15:31
|
15 | if my_macro!("world!") != "Hello world!" {
| ^^^^^^^^^^^^^^ expected (), found reference
|
= note: expected type `()`
found type `&'static str`
error: aborting due to previous error
For more information about this error, try `rustc --explain E0308`.
Which, you know. I get. I understand what the problem is, but I don't understand how to write a macro that will satisfy the code. I can change the code I'm testing against, but that's not what the test wants me to do. I'm only to write a macro. I'm stumped. I also don't understand how encapsulating the macro in a module is meant to help, but the test says it's a test on modules as well as macros.
println! will print to the stdout. Instead, you just want to format the string and return it from the macro. Use format! instead, and drop the ; so that it will return the expression instead of ():
macro_rules! my_macro {
($val:expr) => {
format!("Hello {}", $val)
}
}
Is it possible in rust to write a macro that creates other macros. For example, suppose I define the following two macros:
macro_rules! myprint(
($a:expr) => (print!("{}", $a))
)
macro_rules! myprintln(
($a:expr) => (println!("{}", $a))
)
Since the two macros repeat a lot of code, I may want to write a macro to generate the macros.
I've tried to generate such a meta macro.
#![feature(macro_rules)]
macro_rules! metamacro(
($i:ident) => (
macro_rules! $i (
($a:expr) => ({println!("hello {}", $a)})
)
);
)
metamacro!(foo)
fn main() {
foo!(1i);
}
but get the following errors:
<anon>:6:13: 6:14 error: unknown macro variable `a`
<anon>:6 ($a:expr) => ({println!("hello {}", $a)})
^
playpen: application terminated with error code 101
Program ended.
Edit: After playing around with the macros some more, I discovered that a higher order macro works as expected if the returned macro doesn't receive any arguments. For example, the following code
#![feature(macro_rules)]
macro_rules! metamacro(
($i:ident) => (
macro_rules! $i (
() => ({println!("hello")})
)
);
)
metamacro!(foo)
fn main() {
foo!();
}
prints hello
This was recently made possible in #34925. The answer below refers to older versions of Rust.
This is #6795 and #6994, and isn't directly possible at the moment. E.g. the first thing one would try is
#![feature(macro_rules)]
macro_rules! define {
($name: ident, $other: ident) => {
macro_rules! $name {
($e: expr) => {
$other!("{}", $e)
}
}
}
}
define!{myprintln, println}
define!{myprint, print}
fn main() {}
But this fails with
so8.rs:6:13: 6:14 error: unknown macro variable `e`
so8.rs:6 ($e: expr) => {
^
(Filed #15640 about the caret pointing at the ( instead of the $e.)
The reason is macro expansion is "dumb": it naively interprets and replaces all the tokens, even inside nested macro invocations (and macro_rules! is a macro invocation). Hence, it can't tell that the $e inside the interior macro_rules! are actually meant to be left as $e: it's just trying to replace them when expanding define.
The second thing one might try is passing in extra arguments to define to place in the interior definition, so that the $e doesn't appear directly in the nested invocation:
#![feature(macro_rules)]
macro_rules! define {
($name: ident, $other: ident, $($interior: tt)*) => {
macro_rules! $name {
($($interior)*: expr) => {
$other!("{}", $($interior)*)
}
}
}
}
define!{myprintln, println, $e}
define!{myprint, print, $e}
fn main() {}
That is, I added the $interior: tt to allow capturing the $e. tt stands for token tree, which is a non-delimiter raw token (e.g. $ or some_ident) or a sequence of tokens surrounded by matching delimiters (e.g. (foo + bar fn "baz")). Unfortunately, it seems expansion is eager enough for the $e expansion to be expanded too:
so8.rs:8:13: 8:14 error: unknown macro variable `e`
so8.rs:8 ($($interior)*: expr) => {
^
It works fine when the nested macro has no argument itself, because there is no nested $... non-terminals and so the expansion phase doesn't ever hit the definition problem described above.
Lastly, you can get some semblance of code sharing since you can expand to macro invocations by name:
#![feature(macro_rules)]
macro_rules! my {
($name: ident, $e: expr) => {
$name!("{}", $e)
}
}
fn main() {
my!(print, 1i);
my!(println, 2i);
}
which prints 12.
Here is one example that works, perhaps a starting point for you:
#![feature(macro_rules)]
macro_rules! metamacro(
($i:ident) => (
macro_rules! $i (
() => ({println!("hello!")})
)
);
)
metamacro!(hello)
fn main() {
hello!();
}