Rust 如何有效地从计算部分构建字节数组?

Rust 如何有效地从计算部分构建字节数组?,rust,macros,rust-no-std,Rust,Macros,Rust No Std,我需要构建一个字节数组来表示设备的命令。它可能看起来像这样: let cmds=[ 0x01,//cmd 1 0x02,//cmd 2 0x03,0xaa,0xbb,//cmd 3 0x04,//cmd 4 0x05,0xaa,//cmd 5 ]; 有些命令接受参数,有些不接受参数。有些参数需要计算。每个命令的大小都是固定的,因此在编译时就知道数组需要多大 最好这样构造,我将字节组抽象为命令: let cmds=[ cmd1(), cmd2(), cmd3(0,true,[3,4]), cmd

我需要构建一个字节数组来表示设备的命令。它可能看起来像这样:

let cmds=[
0x01,//cmd 1
0x02,//cmd 2
0x03,0xaa,0xbb,//cmd 3
0x04,//cmd 4
0x05,0xaa,//cmd 5
];
有些命令接受参数,有些不接受参数。有些参数需要计算。每个命令的大小都是固定的,因此在编译时就知道数组需要多大

最好这样构造,我将字节组抽象为命令:

let cmds=[
cmd1(),
cmd2(),
cmd3(0,true,[3,4]),
cmd4(),
cmd5(0xaa)
];
我还没有找到任何方法用函数或宏来实现这一点。我在
no\u std
,所以我不使用集合


如何在Rust中实现类似的功能?

您可以让每个命令函数返回一个数组或
Vec
字节:

fn cmd1() -> [u8; 1] { [0x01] }
fn cmd2() -> [u8; 1] { [0x02] }
fn cmd3(_a: u8, _b: bool, _c: [u8; 2]) -> [u8; 3] { [0x03, 0xaa, 0xbb] }
fn cmd4() -> [u8; 1] { [0x04] }
fn cmd5(a: u8) -> Vec<u8> { vec![0x05, a] }
这将构建一个字节片数组。要获取完整的字节流,请使用
展平

println!("{:?}", cmds);
println!("{:?}", cmds.iter().copied().flatten().collect::<Vec<_>>());
您可以通过返回一些实现
命令的类型
trait并将它们收集到trait对象数组中,使这一点更加复杂,但我将把这留给OP


编辑:这里有一个宏,可以使用板条箱直接构建阵列:

如果您担心性能,本例将数组编译为一条指令:

movabs  rax, -6195540508320529919 // equal to [0x01‬, 0x02, 0x03, 0xAA, 0xBB, 0x04, 0x05, 0xAA]

在电视上看到它。它仅限于
Copy
类型。必须提供数组的长度。如果数组大小与结果的组合大小不匹配,它将在运行时死机。

如果作为宏执行,则无需外部依赖:

macro_rules! cmd_array {
    (@ [ $($acc:tt)* ]) => { [ $($acc)* ] };
    (@ [ $($acc:tt)* ] cmd1(), $($tail:tt)*) => { cmd_array!{@ [ $($acc)* 0x01, ] $($tail)* } };
    (@ [ $($acc:tt)* ] cmd2(), $($tail:tt)*) => { cmd_array!{@ [ $($acc)* 0x02, ] $($tail)* } };
    (@ [ $($acc:tt)* ] cmd3 ($a:expr, $b:expr, $c:expr), $($tail:tt)*) => { cmd_array!{@ [ $($acc)* 0x03, 0xaa, 0xbb, ] $($tail)* } };
    (@ [ $($acc:tt)* ] cmd4(), $($tail:tt)*) => { cmd_array!{@ [ $($acc)* 0x04, ] $($tail)* } };
    (@ [ $($acc:tt)* ] cmd5 ($a:expr), $($tail:tt)*) => { cmd_array!{@ [ $($acc)* 0x05, $a, ] $($tail)* } };
    ($($tail:tt)*) => {
        cmd_array!(@ [] $($tail)*)
    }
}

fn main() {
    let cmds: [u8; 8] = cmd_array![
        cmd1(),
        cmd2(),
        cmd3(0, true, [3, 4]),
        cmd4(),
        cmd5(0xaa),
    ];
    
    println!("{:?}", cmds);
}

此宏是使用来分析命令的,并结合来生成最终数组。

是否要将函数存储在数组中?您可以使用trait对象。@IbraheemAhmed否,不是函数。请通读。如果你能在夜间运行,这个板条箱可能会有所帮助。谢谢,但有了扁平和Vec,我需要std或分配器,对吗?我没有这些。你不必使用
Vec
,而且
flatte
本身也不使用任何分配
flatte
之后,需要收集数据,我认为不可能收集到数组中。不要因为
no\u std
而认为此解决方案有效。如果我有可用的
std
,我只需将命令附加到
Vec
的后面。虽然我怀疑基于宏的实现比我建议的更符合人体工程学。宏方法是我试图找到的,但宏无法返回数组的一部分,例如3个字节。对于C预处理器来说,这将是微不足道的,但在Rust中则不然。
movabs  rax, -6195540508320529919 // equal to [0x01‬, 0x02, 0x03, 0xAA, 0xBB, 0x04, 0x05, 0xAA]
macro_rules! cmd_array {
    (@ [ $($acc:tt)* ]) => { [ $($acc)* ] };
    (@ [ $($acc:tt)* ] cmd1(), $($tail:tt)*) => { cmd_array!{@ [ $($acc)* 0x01, ] $($tail)* } };
    (@ [ $($acc:tt)* ] cmd2(), $($tail:tt)*) => { cmd_array!{@ [ $($acc)* 0x02, ] $($tail)* } };
    (@ [ $($acc:tt)* ] cmd3 ($a:expr, $b:expr, $c:expr), $($tail:tt)*) => { cmd_array!{@ [ $($acc)* 0x03, 0xaa, 0xbb, ] $($tail)* } };
    (@ [ $($acc:tt)* ] cmd4(), $($tail:tt)*) => { cmd_array!{@ [ $($acc)* 0x04, ] $($tail)* } };
    (@ [ $($acc:tt)* ] cmd5 ($a:expr), $($tail:tt)*) => { cmd_array!{@ [ $($acc)* 0x05, $a, ] $($tail)* } };
    ($($tail:tt)*) => {
        cmd_array!(@ [] $($tail)*)
    }
}

fn main() {
    let cmds: [u8; 8] = cmd_array![
        cmd1(),
        cmd2(),
        cmd3(0, true, [3, 4]),
        cmd4(),
        cmd5(0xaa),
    ];
    
    println!("{:?}", cmds);
}