Pass a static string to a macro_rule in rust - rust

I wrote the following code :
macro_rules! my_macro{
("A") => {
println!("Macro called !")
}
}
fn main(){
static test: &'static str = "A";
my_macro!(test);
}
but I have the following error :
error: no rules expected the token `test`
--> test.rt:9:19
|
1 | macro_rules! my_macro{
| --------------------- when calling this macro
...
9 | my_macro!(test);
| ^^^^ no rules expected this token in macro call
error: aborting due to previous error
However, it works fine if I directly call my_macro("A"). Is it possible to fix this ?

Is it possible to fix this ?
No. Macros are expanded at compile time before item names are resolved, therefore your macro has no idea what the value of test is (and would have no idea even if it were a const rather than a static).

so the first problem here is that you macro expects a pattern of "A" not a variable that contains "A"
when you create macros you define certain patterns and follow those patterns in your case your macro must always have "A" in it but it is not a string a it is a pattern of double quote followed by capital a followed by another double quote
If you want to pass a value you should use variable syntax and define what it should expect such as ($a:expr)=>{...}
here you can see all magic tokens possible just scroll down a bit on that docs there are a lot of great examples
PS. here is a macro I use for responding from my endpoints
macro_rules! resp {
(ok) => {
|_| actix_web::HttpResponse::Ok().body(r#"{"success":true}"#)
};
(ok,$data:expr) => {
|_| actix_web::HttpResponse::Ok().json(serde_json::json!({"success":true,"data":$data}))
};
(ok,) => {
|d| actix_web::HttpResponse::Ok().json(serde_json::json!({"success":true,"data":d}))
};
}

Related

Why does a trailing comma prevent this macro from compiling?

I am trying to learn macros, to that effect I made this example:
macro_rules! Reflect
{
($name:ident {$($field_name:ident : $field_type:ty), *}) =>
{
struct $name
{
$($field_name : $field_type), *
}
};
}
Reflect!(
Test
{
field : usize,
field2 : usize,
}
);
This exact syntax generates:
|
183 | macro_rules! Reflect
| -------------------- when calling this macro
...
202 | }
| ^ no rules expected this token in macro call
But if I take out the last coma after field2 I can compile no problem. That makes no sense to me, the matching pattern has a comma at the end, why is it not always looking for a trailing comma?
The way to write a macro which allows a trailing comma, but does not require it, is:
$($field_name:ident : $field_type:ty),* $(,)?
That is: a comma-separated repetition, followed by an optional comma.
Apparently one must do this instead:
macro_rules! Reflect
{
($name:ident {$($field_name:ident : $field_type:ty,) *}) =>
{
struct $name
{
$($field_name : $field_type), *
}
};
}
Notice the comma moved.

How can i return a formatted string from a rust macro

I am trying to achieve something like this:
Construct an error message with some captured variables;
Log this error message;
Save and return this error message to user eventually;
Apparently this can be done in a few lines of code, but it is a bit tedious since i need this pattern heavily everywhere in my project, i mean like below:
let var_a = 233;
let err = format!("some custom error message, var_a: {}", var_a);
// Here i do the logging with the `tokio-rs/tracing` macros.
error!(err = %err, "some custom error message");
// The s_errs will be eventually showed to end users.
s_errs.append(err);
I am trying to achieve this with a macro which wraps the tokio-rs/tracing macros but return the constructed err message:
#[macro_export]
macro_rules! log_usr_err {
($($arg:tt)+) => (
error!($($arg)+)
format!($($arg)+)
);
}
so i can just use this macro in my project to simplify the coding a bit:
let err = log_usr_err!("some custom error message, {}", var_a = var_a);
// The s_errs will be eventually showed to end users.
s_errs.append(err);
but stuck at the compile error:
error: macro expansion ignores token `format` and any following
How should i return the formatted string from my log_urs_err! macro?
Coz in the future in want to include the accurate file! info and the line! info also in my logs, thus i think via macro is the right direction to go, or there is some better approach?
Let's just try and look at what the expanded code would look like in your case. Reiterating the code in question:
#[macro_export]
macro_rules! log_usr_err {
($($arg:tt)+) => (
error!($($arg)+)
format!($($arg)+)
);
}
let err = log_usr_err!("some custom error message, {}", var_a = var_a);
If we try to literally substitute the result of macro expansion, we'd get the following:
let err = error!("some custom error message, {}", var_a = var_a)
format!("some custom error message, {}", var_a = var_a);
This is obviously not valid Rust.
The problem is that your macro is currently written to expand to the list of statements, i.e. to some self-contained part of the code (of course, after adding the necessary semicolon between error and format). It can be, for example, a function body (playground):
macro_rules! log_usr_err {
($($arg:tt)+) => (
error!($($arg)+);
format!($($arg)+)
);
}
fn err() {
log_usr_err!("test");
}
But you'd like to use this macro in expression position, i.e. to assign the value returned from it to the variable. For this, macro must expand to the expression, not to statements.
So, what to do? The change is, in fact, quite simple: a set of statements can be converted to expression by wrapping them in a block, since blocks are expressions:
macro_rules! log_usr_err {
($($arg:tt)+) => ({
error!($($arg)+);
format!($($arg)+)
});
}
Note the extra braces around the macro content.
With this change, your code compiles.

How to match an argument with dots in Rust macros?

I am writing a program and it contains a lot of matchblocks as I keep calling methods and functions that return Result struct type results.
So I was thinking maybe a macro will reduce the amount of code.
And the final macro is like this:
#[macro_export]
macro_rules! ok_or_return {
//when calls on methods
($self: ident, $method: ident($($args: tt)*), $Error: ident::$err: ident) => {{
match $self.$method($($args)*) {
Ok(v) => v,
Err(e) => {
dbg!(e);
return Err($Error::$err);
}
}
}};
}
As yo can see, I use $($args: tt)* to match multiple arguments, and it goes pretty well. Even when I use struct.method() as a form of argument, it compiled.
Like:
ok_or_return!(self, meth(node.get_num()), Error::GetNumError);
However, if I use the same form to match a normal macro argument, it failed. I changed the specifier to tt, and it didn't work out. Like:
ok_or_return!(self.people, meth(node.get_num()), Error::GetNumError);
So my problem is why node.get_num() can be matched and self.people can't?

Why does a match never use the second match arm?

I am trying to make reimplementation of first 2 Final Fantasy games using original data from various platforms. I want to get 2 program arguments using the getopts crate and handle both of them by using match but it just executes the first match element. I think I screwed up something with types.
Maybe there is another way to do it? I am lost using the official Rust docs and any tutorials on internet are not really noob-friendly.
Here is the code:
let args: Vec<String> = env::args().map(|x| x.to_string()).collect();
if(args.len() < 3) {
println!("=====ERROR=====\nInvalid number of parameters\nExpected: <gamename> <gamerom>\nType in: 'help me' to get some help.");
process::exit(1);
}
let ref game = args[1];
let ref rom = args[2];
match game {
help => {
println!("=====HELP======");
match rom {
list => println!("Available games: ff1, ff2\nAvailable roms: ff1_j_msx, ff1_j_nes, ff1_u, ff1and2, ff2_j, ff2_u_proto"),
me => println!("Available help commands:\nlist -> List of available games and roms.\nme -> This help"),
_ => println!("=====ERROR=====\nInvalid help command.")
}
},
_ => println!("=====ERROR=====\nInvalid game, type in 'help me' to get some help.")
}
You really need to read the compilers error and warning messages. This code has seven warnings. If you had addressed any of them you'd be a lot closer to fixing the problem yourself. If you'd fixed all of them, your problem would be gone.
Here's a representative warning where the compiler tells you exactly what the problem is:
warning: unreachable pattern
--> src/main.rs:24:5
|
24 | _ => println!("=====ERROR=====\nInvalid game, type in 'help me' to get some help.")
| ^ this is an unreachable pattern
|
= note: #[warn(unreachable_patterns)] on by default
note: this pattern matches any value
--> src/main.rs:15:5
|
15 | help => {
| ^^^^
When you use just help, that creates a new variable with the value you are matching on. In this case, it matches everything, so the subsequent arms can never match.
Instead, you need to match against a string literal:
match game.as_str() {
"help" => {
match rom.as_str() {
"list" => /* ... */,
"me" => /* ... */,
_ => /* ... */,
}
},
_ => /* ... */,
}
I'd strongly encourage you to go back and re-read The Rust Programming Language. It's where a lot of the beginner documentation is kept. Specifically, you should read from the beginning and then up through the chapter on match and the chapter on patterns.

Error 'cannot move out of dereference' when trying to match strings from vector

I am very new to rust and trying to write a command line utility as a way to learn.
I am getting the list of args and trying to match on them
let args = os::args()
//some more code
match args[1].into_ascii_lower().as_slice() {
"?" | "help" => { //show help },
"add" => { //do other stuff },
_ => { //do default stuff }
}
this causes this error
cannot move out of dereference (dereference is implicit, due to indexing)
match args[1].into_ascii_lower().as_slice() {
^~~~~~~
I have no idea what that means, but searching yield this which I didn't completely get, but changing the args[1] to args.get(1) gives me another error
error: cannot move out of dereference of `&`-pointer
match args.get(1).into_ascii_lower().as_slice() {
^~~~~~~~~~~
what's going on?
As you can see in the documentation, the type of into_ascii_lower() is (see here) :
fn into_ascii_upper(self) -> Self;
It takes self directly, not as a reference. Meaning it actually consumes the String and return an other one.
So, when you do args[1].into_ascii_lower(), you try to directly consume one of the elements of args, which is forbidden. You probably want to make a copy of this string, and call into_ascii_lower() on this copy, like this :
match args[1].clone().into_ascii_lower().as_slice() {
/* ... */
}

Resources