在编译时获取已知长度的片段

时间:2016-07-03 10:21:16

标签: 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]));    
}

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

3 个答案:

答案 0 :(得分:4)

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

fn unpack_u32(data: [u8; 4]) -> u32 { ... }

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

unpack_u32([v[1], v[2], v[3], v[4]]);

但是输入相当难看,并且不能很好地扩展数组大小。所以问题是"How to get a slice as an array in Rust?"。我使用了稍微修改过的Matthieu M.对所述问题(playground)的回答:

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])));    
}

如您所见,仍然存在assert因此可能导致运行时失败。 Rust编译器无法知道v[1..5]是4个元素长,因为1..5只是Range的语法糖,这只是编译器没有什么特别之处的类型。

答案 1 :(得分:2)

我认为答案是否定的;切片的大小(或最小大小)不是类型的一部分,因此编译器无需检查;类似地,向量是动态调整大小的,因此在编译时无法检查是否可以采用正确大小的切片。

我可以看到信息在编译时原则上可用的唯一方法是将函数应用于编译时已知数组。我认为你仍然需要实现一个程序宏来进行检查(所以每晚只有Rust,这并不容易)。

如果问题是效率而不是编译时检查,您可以调整代码,以便例如在n*4调用之前检查n元素是否可用。你的功能;您可以使用不安全的get_unchecked来避免以后的冗余边界检查。显然,您需要小心避免执行中的错误。

答案 2 :(得分:2)

我有一个类似的问题,在堆栈上创建一个固定的字节数组,对应于其他字节数组的const长度(在开发时可能会改变)

编译器插件和宏的组合是解决方案:

https://github.com/frehberg/rust-sizedbytes