rust - 使用宏初始化一大堆非复制元素

我试图用相同的初始化器初始化一个很大的元素数组。64个元素只是一个例子-我想让它至少16k。不幸的是

let array : [AllocatedMemory<u8>; 64] = [AllocatedMemory::<u8>{mem:&mut []};64];

不会工作,因为AllocatedMemory结构未实现Copy
error: the trait `core::marker::Copy` is not implemented for the type `AllocatedMemory<'_, u8>` [E0277]
let array : [AllocatedMemory<u8>; 64] = [AllocatedMemory::<u8>{mem:&mut []}; 64];
                                        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

所以我尝试了宏,但没有成功:
struct AllocatedMemory<'a, T: 'a> {
    mem: &'a mut [T],
}

macro_rules! init_memory_helper {
    (1, $T : ty) => { AllocatedMemory::<$T>{mem: &mut []} };
    (2, $T : ty) => { init_memory_helper!(1, $T), init_memory_helper!(1, $T) };
    (4, $T : ty) => { init_memory_helper!(2, $T), init_memory_helper!(2, $T) };
    (8, $T : ty) => { init_memory_helper!(4, $T), init_memory_helper!(4, $T) };
    (16, $T : ty) => { init_memory_helper!(8, $T), init_memory_helper!(8, $T) };
    (32, $T : ty) => { init_memory_helper!(16, $T), init_memory_helper!(16, $T) };
    (64, $T : ty) => { init_memory_helper!(32, $T), init_memory_helper!(32, $T) };
}

macro_rules! init_memory {
    (1, $T : ty) => { [init_memory_helper!(1, $T)] };
    (2, $T : ty) => { [init_memory_helper!(2, $T)] };
    (4, $T : ty) => { [init_memory_helper!(4, $T)] };
    (8, $T : ty) => { [init_memory_helper!(8, $T)] };
    (16, $T : ty) => { [init_memory_helper!(16, $T)] };
    (32, $T : ty) => { [init_memory_helper!(32, $T)] };
    (64, $T : ty) => { [init_memory_helper!(64, $T)] };
}

fn main() {
    let array: [AllocatedMemory<u8>; 64] = init_memory!(64, u8);
    println!("{:?}", array[0].mem.len());
}

错误消息是
error: macro expansion ignores token `,` and any following
    (64, $T : ty) => { init_memory_helper!(32, $T), init_memory_helper!(32, $T) };
note: caused by the macro expansion here; the usage of `init_memory_helper!` is likely invalid in expression context

有没有什么方法可以在不剪切和粘贴每个初始值设定项的情况下初始化这个数组?

最佳答案

问题是the expansion of a macro absolutely must be a complete and independently valid grammar element。您不能扩展到a, b而不能扩展到42 +。也没有办法(静态地)在Rust中连接或连接数组;整个数组初始化器必须在一个步骤中扩展到。
这可以使用push-down accumulation的宏来完成。诀窍是在递归中传递尚未语法有效的部分数组表达式,而不是在返回时构造。当您到达扩展的底部时,您将一次性发出现在完整的表达式。
下面是一个宏,它支持长度为0到8,幂为2到64的数组:

macro_rules! array {
    (@accum (0, $($_es:expr),*) -> ($($body:tt)*))
        => {array!(@as_expr [$($body)*])};
    (@accum (1, $($es:expr),*) -> ($($body:tt)*))
        => {array!(@accum (0, $($es),*) -> ($($body)* $($es,)*))};
    (@accum (2, $($es:expr),*) -> ($($body:tt)*))
        => {array!(@accum (0, $($es),*) -> ($($body)* $($es,)* $($es,)*))};
    (@accum (3, $($es:expr),*) -> ($($body:tt)*))
        => {array!(@accum (2, $($es),*) -> ($($body)* $($es,)*))};
    (@accum (4, $($es:expr),*) -> ($($body:tt)*))
        => {array!(@accum (2, $($es,)* $($es),*) -> ($($body)*))};
    (@accum (5, $($es:expr),*) -> ($($body:tt)*))
        => {array!(@accum (4, $($es),*) -> ($($body)* $($es,)*))};
    (@accum (6, $($es:expr),*) -> ($($body:tt)*))
        => {array!(@accum (4, $($es),*) -> ($($body)* $($es,)* $($es,)*))};
    (@accum (7, $($es:expr),*) -> ($($body:tt)*))
        => {array!(@accum (4, $($es),*) -> ($($body)* $($es,)* $($es,)* $($es,)*))};
    (@accum (8, $($es:expr),*) -> ($($body:tt)*))
        => {array!(@accum (4, $($es,)* $($es),*) -> ($($body)*))};
    (@accum (16, $($es:expr),*) -> ($($body:tt)*))
        => {array!(@accum (8, $($es,)* $($es),*) -> ($($body)*))};
    (@accum (32, $($es:expr),*) -> ($($body:tt)*))
        => {array!(@accum (16, $($es,)* $($es),*) -> ($($body)*))};
    (@accum (64, $($es:expr),*) -> ($($body:tt)*))
        => {array!(@accum (32, $($es,)* $($es),*) -> ($($body)*))};

    (@as_expr $e:expr) => {$e};

    [$e:expr; $n:tt] => { array!(@accum ($n, $e) -> ()) };
}

fn main() {
    let ones: [i32; 64] = array![1; 64];
    println!("{:?}", &ones[..]);
}

这里的策略是将输入的大小乘以2的幂,并将非2的幂的余数相加。这是为了避免宏递归限制(我相信默认值是64),方法是确保$n的值快速下降。
只是为了避免经常出现的后续问题:不,不能用算术来简化;不能用宏来做算术。:)
补遗:如果您不确定这是如何工作的,可以在编译时将-Z trace-macros传递给rustc,并查看扩展的每个宏调用。以array![1; 6]为例,可以得到如下结果:
array! { 1 ; 6 }
array! { @ accum ( 6 , 1 ) -> (  ) }
array! { @ accum ( 4 , 1 ) -> ( 1 , 1 , ) }
array! { @ accum ( 2 , 1 , 1 ) -> ( 1 , 1 , ) }
array! { @ accum ( 0 , 1 , 1 ) -> ( 1 , 1 , 1 , 1 , 1 , 1 , ) }
array! { @ as_expr [ 1 , 1 , 1 , 1 , 1 , 1 , ] }

本文翻译自 https://stackoverflow.com/questions/36258417/

网站遵循 CC BY-SA 4.0 协议,转载或引用请注明出处。

标签 rust initializer-list static-initializer


相关文章:

rust - 在Rust中将字符串开头的多个字符合并为一个

c++ - 在C ++中迭代非const变量

oop - Kotlin - 是否可以在类中的init块之前初始化伴随对象?

java - 为什么内部类不能使用静态初始化器?

c++ - 在库中查找所有动态初始化

rust - 如何在Rust中定义泛型整数类型的无限范围?

macos - OpenSSL箱子无法在Mac OS X 10.11上编译

rust - 共享弱特征对象引用

c++ - 使用空初始化列表直接初始化

c++ - 将int转换为size_t