#rust #rust-macros
#Ржавчина #rust-макросы
Вопрос:
Я пытаюсь написать макрос для переключения между rayon par_iter
и std iter
в зависимости от функции сборки (возможно, забегая вперед, поскольку я еще мало читал о макросах). Здесь с макросом, кажется, немного лучше иметь дело, чем с функцией, поскольку для выполнения этой работы функции могут потребоваться некоторые относительно сложные типы; кроме того, макрос может оставаться более гибким в будущем, если я захочу добавить еще больше вариаций в функциях сборки, относящихся к тому, как запускать итераторы.
#[macro_export]
macro_rules! par_iter {
($($tokens:tt)*) => {
#[cfg(feature = "threaded")]
$($tokens)*.par_iter()
#[cfg(not(feature = "threaded"))]
$($tokens)*.iter()
}
}
Я вижу следующую ошибку:
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
Хотя я понятия не имею о первой ошибке, мне любопытно, почему ;
ожидается a — как мне сделать его допустимым в контексте выражения?
Комментарии:
1. Я полагаю, вы не хотели комментировать
#[cfg(not(feature = "threaded"))]
?2. Извините, да, это была ошибка при экспериментировании. Исправлено.
Ответ №1:
По сути, это сводится к тому, что вам не разрешено иметь атрибуты в таких выражениях, например, что следующее недопустимо:
b_slice.iter()
#[cfg(not(feature = "threaded"))]
.map(|x| x)
.collect();
Чтобы устранить проблему, вы можете назначить их временной переменной, например:
Обратите внимание на двойное {{
и }}
, которое приводит к блоку, так что конечным выражением является значение, которое приводит к блоку.
#[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
}};
}
Кроме того, вы также можете разделить его на два таких макроса:
#[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()
}
}
Комментарии:
1. Я попробовал первый, но у меня был только один
{
вместо двойного{{
! Я думаю, это имеет смысл, поскольку внешний{
должен находиться только в макросе.2. Точно, с double
{{
вы оборачиваете все в блок, а затем конечнымit
является результат блока. (Я обновил ответ, чтобы указать на это для будущих читателей.)