取编译时已知的特定长度的切片

use*_*932 1 rust

在这段代码中:

fn unpack_u32(data: &[u8]) -> u32 {
    assert_eq!(data.len(), 4);
    let res = data[0] as u32 |
    (data[1] as u32) << 8 |
    (data[2] as u32) << 16 |
        (data[3] as u32) << 24;
    res
}

fn main() {
    let v = vec![0_u8, 1_u8, 2_u8, 3_u8, 4_u8, 5_u8, 6_u8, 7_u8, 8_u8];
    println!("res: {:X}", unpack_u32(&v[1..5]));    
}
Run Code Online (Sandbox Code Playgroud)

该函数unpack_u32只接受长度为 4 的切片。有没有办法assert_eq用编译时检查替换运行时检查?

Luk*_*odt 5

是的,有点。第一步很简单:将参数类型从 更改&[u8][u8; 4]

fn unpack_u32(data: [u8; 4]) -> u32 { ... }
Run Code Online (Sandbox Code Playgroud)

但是将切片(如&v[1..5])转换为类型的对象是[u8; 4]很困难的。您当然可以通过指定所有元素来创建这样的数组,如下所示:

unpack_u32([v[1], v[2], v[3], v[4]]);
Run Code Online (Sandbox Code Playgroud)

但这很难打字,并且不能很好地随数组大小扩展。所以问题是“如何在 Rust 中将切片作为数组获取?” . 我使用了 Matthieu M. 对上述问题(操场)的回答的略微修改版本:

fn unpack_u32(data: [u8; 4]) -> u32 {
    // as before without assert
}

use std::convert::AsMut;

fn clone_into_array<A, T>(slice: &[T]) -> A
    where A: Default + AsMut<[T]>,
          T: Clone
{
    assert_eq!(slice.len(), std::mem::size_of::<A>()/std::mem::size_of::<T>());

    let mut a = Default::default();
    <A as AsMut<[T]>>::as_mut(&mut a).clone_from_slice(slice);
    a
}

fn main() {
    let v = vec![0_u8, 1, 2, 3, 4, 5, 6, 7, 8];
    println!("res: {:X}", unpack_u32(clone_into_array(&v[1..5])));    
}
Run Code Online (Sandbox Code Playgroud)

如您所见,仍然存在assert运行时失败的可能性。Rust 编译器无法知道v[1..5]4 个元素的长度,因为1..5这只是语法糖,Range编译器对此类型一无所知。