Reputation: 31
I have a macro to generate match
arms:
macro_rules! sort_by {
( $query:ident, $sort_by:expr, { $( $name:pat => $column:path,)+ } ) => {
match $sort_by.column {
$(
$name => if $sort_by.descending {
$query = $query.order_by($column.desc());
} else {
$query = $query.order_by($column.asc());
},
)+
}
}
}
and I want to call it like this:
sort_by!(query, sort_by.unwrap_or(Sort::desc("id")), {
"id" => table::id,
"customerName" => table::customer_name,
});
But I'm getting an error:
sort_by!(query, &sort_by.unwrap_or(Sort::desc("id")), {
^^^^^^^ value moved here in previous iteration of loop
So I have to call it like this:
let sort = sort_by.unwrap_or(Sort::desc("id"));
sort_by!(query, &sort, {
"id" => table::id,
"customerName" => table::customer_name,
});
What should I change to be able to use the expression directly in the macro invocation?
Upvotes: 2
Views: 1714
Reputation: 602685
Using a macro is equivalent to substituting the code it expands to into its call site. This means if the macro expansion contains $sort_by
multiple times, the code will evaluate the expression you pass in as $sort_by
multiple times. If the expression consumes some variable, this will be invalid.
This is in contrast to how function calls work. If you pass an expression to a function, it will be evaluated before calling the function, and only the result is passed to the function.
If this is the source of your problem, you can fix it by assigning $sort_by
to a local variable inside your macro expansion, and only access the local variable subsequently:
macro_rules! sort_by {
($query:ident, $sort_by:expr, { $($name:pat => $column:path,)+ }) => {
let sort_by = $sort_by;
match sort_by.column {
$(
$name => if sort_by.descending {
$query = $query.order_by($column.desc());
} else {
$query = $query.order_by($column.asc());
},
)+
}
}
}
(Note that I could not test this, since your example is incomplete.)
Upvotes: 3