在 Rust 中使用宏创建总和直到 N
Create sum of ones till N using macro in Rust
我想创建一个宏来将 count!(5)
转换为 1+1+1+1+1
。
最后一个原因是在结构定义中使用 count!(N) - 1
,其中 N
是常量泛型。
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
.
的错误
如何更改我的代码以使其正确?
遗憾的是,目前的 stable Rust 是不可能的。当前 Rust 对 const 泛型的支持是 MVP,在稳定版本中不支持具有复杂表达式的 const 泛型。
但是,使用 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 link of the example above
您可以在此 official Rust blog post about const generics 中查看更多详细信息。
宏扩展适用于原始标记。如果按照宏展开过程...
#![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()
}
然而,这可能不是你想要的。
我想创建一个宏来将 count!(5)
转换为 1+1+1+1+1
。
最后一个原因是在结构定义中使用 count!(N) - 1
,其中 N
是常量泛型。
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
.
如何更改我的代码以使其正确?
遗憾的是,目前的 stable Rust 是不可能的。当前 Rust 对 const 泛型的支持是 MVP,在稳定版本中不支持具有复杂表达式的 const 泛型。
但是,使用 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 link of the example above
您可以在此 official Rust blog post about const generics 中查看更多详细信息。
宏扩展适用于原始标记。如果按照宏展开过程...
#![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()
}
然而,这可能不是你想要的。