【问题标题】:Take slice of certain length known at compile time获取编译时已知的一定长度的切片
【发布时间】:2016-09-21 14:40:42
【问题描述】:

在这段代码中:

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

【问题讨论】:

    标签: rust


    【解决方案1】:

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

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

    但是将切片(如&amp;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 的语法糖,它只是编译器不知道的一个类型。

    【讨论】:

      【解决方案2】:

      我认为答案是否定的;切片没有大小(或最小大小)作为类型的一部分,因此编译器无需检查;同样,向量是动态调整大小的,因此无法在编译时检查您是否可以获取正确大小的切片。

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

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

      【讨论】:

        【解决方案3】:

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

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

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-09-16
          • 1970-01-01
          • 1970-01-01
          • 2018-02-15
          • 1970-01-01
          相关资源
          最近更新 更多