#rust #macros #rust-macros
#Ржавчина #макросы #rust-макросы
Вопрос:
Я хотел бы создать макрос для преобразования count!(5)
в 1 1 1 1 1
. Последняя причина заключается в использовании count!(N) - 1
в определении структуры, где N
const generics .
macro_rules! count {
(1) => {0};
(2) => { 1 count!(1)};
($n: tt) => { 1 count!($n - 1)};
}
struct WindowIterator<I, Item, const N: usize> {
iter: I,
values: Box<[Item; count!(N) - 1 ]>,
index: usize,
}
С этим определением я получаю ошибку типа no rules expected the token
N.
Как я могу изменить свой код, чтобы сделать его правильным?
Комментарии:
1. Ваш план не будет работать, поскольку макросы расширяются перед любым вычетом типа или созданием экземпляра шаблона; это и макросы работают только через токены, а не значения. Тем не менее, я озадачен этой идеей в первую очередь, поскольку ваш код компилируется с использованием
N
directly: playground .2. Это похоже на проблему XY .
3. обновлены вопросы. Мне нужно создать массив длиной N-1
Ответ №1:
К сожалению, это невозможно с текущим стабильным Rust. Текущая поддержка Rust для const generics — MVP, а const generics со сложными выражениями не поддерживаются в стабильной сборке.
Однако это возможно с ночной сборкой Rust.
#![feature(generic_const_exprs)]
struct WindowIterator<I, Item, const N: usize>
where
[Item; N - 1]: Sized,
{
iter: I,
values: Box<[Item; N - 1]>,
index: usize,
}
Ссылка на Rust Playground в приведенном выше примере
Вы можете ознакомиться с более подробной информацией в этом официальном сообщении в блоге Rust о const generics.
Комментарии:
1. обновлены вопросы. Мне нужно создать массив длиной N-1
2. @allevo я обновил ответ.
Ответ №2:
Расширение макроса работает с необработанными токенами. Если вы будете следовать процессу расширения макроса…
#![feature(log_syntax)]
macro_rules! count {
(1) => {{
log_syntax!(MatchOne count!(1));
0
}};
(2) => {{
log_syntax!(MatchTwo count!(2));
0
}};
($n:tt) => {{
log_syntax!(MatchNumber count!($n));
1 count!($n - 1)
}};
($($n:tt)*) => {{
log_syntax!(MatchAny count!($($n)*));
}};
}
Выводит:
MatchNumber count! (5)
MatchAny count! (5 - 1)
Второй вызов не получил 4
, кроме 5 - 1
необработанных токенов.
То, что вы хотите, можно сделать с помощью процедурных макросов, например:
extern crate proc_macro;
use itertools::Itertools; // When std's intersperse() will be stable, can get rid of this.
use proc_macro::{Delimiter, Group, Literal, Punct, Spacing, TokenStream, TokenTree};
#[proc_macro]
pub fn count(number: TokenStream) -> TokenStream {
let number = number.to_string().parse().expect("expected a number"); // A simple way to parse int literal without `syn`
let result = if number == 0 {
TokenTree::Literal(Literal::u64_unsuffixed(0))
} else {
TokenTree::Group(Group::new(
Delimiter::None,
std::iter::repeat(TokenTree::Literal(Literal::u64_unsuffixed(1)))
.take(number)
.intersperse(TokenTree::Punct(Punct::new(' ', Spacing::Alone)))
.collect(),
))
};
result.into()
}
Однако, это, вероятно, не то, что вы хотите.