I want to define a variable with a macro ident and pass it to a derive macro, but I get an error:
Error
error: macro expansion ignores token `let` and any following
--> database/src/models.rs:15:9
|
15 | let struct_name = stringify!($name);
| ^^^
...
50 | / model!(Person {
51 | | name: String
52 | | });
| |___- caused by the macro expansion here
|
= note: the usage of `model!` is likely invalid in item context
Code
#[macro_export]
macro_rules! model {
(
$(#[$met: meta])*
$name: ident { $($attr: ident : $typ: ty),* }
) => {
let struct_name = stringify!($name);
//let table_name = struct_name.to_camel_case();
dbg!("{}", struct_name);
#[derive(Debug)]
struct $name {
name: String
};
};
}
model!(Person {
name: String
});
Playground
You use the macro model! where the compiler expects an item (function/struct/trait declaration or impl block). A let statement is only valid inside a code block. This is why your first example works (where the macro call is inside main), while your second example doesn't.
This seems like an instance of the XY problem. Check out procedural macros if you want to run code to generate code at compile time.
Related
I am trying to write a section of code that does a web request every few seconds and updates a struct in rust. I have so far tried using a thread to accomplish this task however I am having some real trouble moving variables into the thread and assigning the results of the web request to a struct once it completes.
I have provided a small code sample below that doesn't include a web request but does demonstrate the problem I am having moving variables around.
Code:
use std::thread;
#[derive(Debug, Clone)]
struct input_holder {
i : String,
}
fn main() {
// pass this string into the thread loop.
let input = String::from("This is a string");
let input_loop = input.clone();
//assign the output of the method to this struct.
let ih = input_holder {
i : input
};
thread::spawn(|| {
let runtime = tokio::runtime::Runtime::new().unwrap();
loop {
let _ = runtime.block_on(runtime.spawn(async move {
let _new_input = match update_string(input_loop.clone(), ih.clone()){
Some(ni) => ni,
None => String::from("None")
};
}));
}
});
}
//This is where I would do the web request. I can test this method outside of the thread and it works as expected.
pub fn update_string(_input: String, mut ih: input_holder) -> Option<String> {
ih.i = String::from("This is an updated string");
Some(String::from("This is an updated string"))
}
Below is the error message I am running into:
error[E0382]: use of moved value: `ih`
--> src/main.rs:64:63
|
64 | let _ = runtime.block_on(runtime.spawn(async move {
| _______________________________________________________________^
65 | | let _new_input = match update_string(input_loop.clone(), ih.clone()){
| | -- use occurs due to use in generator
66 | | Some(ni) => ni,
67 | | None => String::from("None")
68 | | };
69 | | }));
| |_____________^ value moved here, in previous iteration of loop
|
= note: move occurs because `ih` has type `input_holder`, which does not implement the `Copy` trait
There doesn't seem to be anyway for me to pass ih that I am aware of, I don't know what use occurs due to use in generator means and Google does not seem to have much on this error (there are a bunch of options for move occurs due to use in generator but nothing for this error).
I have tried cloning and not cloning, borrowing, removing the move from the loop (I should note here I can't implement the copy trait on the struct). How are you supposed to get variables in and out of a thread loop like this?
I think I might not be understanding something about how you are supposed to move variables around in rust because in general I find myself needing to make lots and lots of copies of any value I plan to pass between methods.
How can I add and use doc comments with a macro invocation?
macro_rules! foo {
(
$(#[$outer:meta])*
$name:ident
) => {
pub mod $name {
$(#[$outer])*
pub const THE_ANSWER: i32 = 42;
}
}
}
/// doc for macro created module
foo!(bar);
fn main() {
println!("{}", bar::THE_ANSWER);
}
Playground Link
I seem to be doing what's recommended by this question but I still get the warning.
warning: unused doc comment
--> src/main.rs:13:1
|
13 | /// doc for macro created module
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ rustdoc does not generate documentation for macro invocations
|
= note: `#[warn(unused_doc_comments)]` on by default
= help: to document an item produced by a macro, the macro must produce the documentation as part of its expansion
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 am trying to nest macro calls to ease converting between rust and c ffi types.
The inner macro generates calls to conversion functions from type names like this:
macro_rules! type_conversion {
($arg_name:ident, bool, BOOL) => (crate::type_wrappers::type_conversion::convert_rust_bool($arg_name));
($arg_name:ident, BOOL, bool) => (crate::type_wrappers::type_conversion::convert_c_bool($arg_name));
}
The outer macro now uses this to generate further conversions.
It basically works like this:
macro_rules! outer {
($arg_name:ident, $type1:ty, $type2:ty) => (type_conversion!($arg_name, $type1, $type2));
}
Now, when i try to use the outer macro like this:
fn outer() {
outer!(hello_world, bool, BOOL);
}
I get this Error:
error: no rules expected the token `bool`
--> src\type_wrappers\type_conversion.rs:252:77
|
202 | macro_rules! type_conversion {
| ---------------------------- when calling this macro
...
252 | ($arg_name:ident, $type1:ty, $type2:ty) => (type_conversion!($arg_name, $type1, $type2));
| ^^^^^^ no rules expected this token in macro call
...
256 | outer!(hello_world, bool, BOOL);
| ------------------------------- in this macro invocation
I cannot make sense out of this error, especially since CLions Macro expansion tool expands this into:
type_conversion!(hello_world , bool , BOOL )
Which is what i would expect and also, what the Error suggests.
Where is the Error in those Macros?
I guess the ty macro type cannot be casted to identifier (which bool & BOOL parameter are) and thus matched. So with ident type it works (playground):
macro_rules! outer {
($arg_name:ident, $type1:ident, $type2:ident) => {
type_conversion!($arg_name, $type1, $type2)
};
}
I want to write a macro that prints "OK" then returns self in a method. It's my first macro, so I tried this, thinking it will just make something like a text replacement, but it fails:
macro_rules! print_ok_and_return_self {
() => {
println!("OK");
self
}
}
fn main() {
let a = A{};
a.a().a();
}
struct A {}
impl A {
fn a(self) -> Self {
print_ok_and_return_self!()
}
}
Error:
error: macro expansion ignores token `self` and any following
--> src/main.rs:4:13
|
4 | self
| ^^^^
|
note: caused by the macro expansion here; the usage of `print_ok_and_return_self!` is likely invalid in expression context
--> src/main.rs:17:13
|
17| print_ok_and_return_self!()
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
After a quick look at the documentation, I know it's not just text replacement, but I still don't know how to make it work.
There are two errors in a row, let's fix the first one.
The syntax for a macro arm is:
(...) => {
...
}
which means that what your macro expands to is:
println!("OK");
self
which is not OK (two statements).
Instead, it should expand to an expression (in this case), which you get by enclosing it within {}:
macro_rules! print_ok_and_return_self {
() => {
{
println!("OK");
self
}
}
}
This leads to the second error:
error[E0424]: `self` is not available in a static method
--> <anon>:4:9
|
4 | self
| ^^^^ not available in static method
...
17 | print_ok_and_return_self!()
| --------------------------- in this macro invocation
|
= note: maybe a `self` argument is missing?
A macro cannot assume the existence of a variable in its scope, so you need to pass self as an argument:
macro_rules! print_ok_and_return_value {
($v:expr) => {{
println!("OK");
$v
}}
}
and the invocation becomes:
impl A {
fn a(self) -> Self {
print_ok_and_return_value!(self)
}
}