Rust:我可以通过以简单的方式在较小的范围内借用整个固定大小的数组来获得固定大小的切片吗

Rust: can I have a fixed size slice by borrowing the whole fixed size array in a smaller scope in a simple way

我看到了解决方法,它们有点长。我是否缺少 Rust 的一个功能或一个简单的解决方案(重要:不是解决方法)。我觉得我应该可以用一个简单的宏来做到这一点,但 arrayref crate 实现不是我想要的。这是一个需要添加到 Rust 的功能,还是在 较小的范围 中从固定大小的数组创建固定大小的切片是不好的。 基本上我想做的是这个;

fn f(arr:[u8;4]){
    arr[0];
}

fn basic(){
    let mut arr:[u8;12] = [0;12];
    // can't I borrow the whole array but but a fixed slice to it?
    f(&mut arr[8..12]); // But this is know on compile time?
    f(&mut arr[8..12] as &[u8;4]); // Why can't I do these things?
}

我想要的可以通过下面的代码实现(来自其他线程)

use array_ref;

fn foo(){
    let buf:[u8;12] = [0;12];
    let (_, fixed_slice) = mut_array_refs![
        &mut buf,
        8,
        4
    ];
    write_u32_into(fixed_slice,0);
}

fn write_u32_into(fixed_slice:&mut [u8;12],num:u32){
    // won't have to check if fixed_slice.len() == 12 and won't panic
}

但我查看了板条箱,尽管它从不恐慌,但仍有许多不安全的块和许多代码行。这是 Rust 本身的解决方法。首先,我想要这样的东西来摆脱检查大小和可能的运行时恐慌的开销。

还有一点开销,没关系,这不是一个有效的答案,因为从技术上讲,即使开销很小,我也应该能够在编译时保证这一点,但这并不意味着不需要生锈拥有这种类型的功能,否则我不应该寻找理想的方式。 注意:这可以用生命周期解决吗?

编辑:如果我们能够对固定切片使用不同的语法,例如 arr[12;;16],当我以这种方式借用它们时,它会借用它会借用整个 arr。我认为这样许多函数(write_u32)将以更“生锈”的方式实现。

为什么它不起作用

你想要的东西在 rust stable 或 nightly 中不可用,因为与 const 相关的多个东西还没有稳定下来,即 const generics 和 const traits。涉及特征的原因是因为 arr[8..12] 是对 core::ops::Index::<Range<usize>> 特征的调用,returns 是对切片的引用,在你的例子中是 [u8]。此类型未确定大小且不等于 [u8; 4],即使编译器可以确定它是,Rust 本质上是安全的,有时可以过度保护以确保安全。

那你能做什么?

你有一些方法可以解决这个问题,我会留在 no_std 环境中来解决这一切,因为那似乎是你工作的地方,并且会避免额外的板条箱。

更改函数签名

您拥有的当前函数签名将四个 u8 作为拥有值。如果您只要求 4 个值,则可以将这些值作为函数的参数。当您需要更大的数组时,此选项会失效,但此时,最好将数组作为参考或使用下面的方法。

最常见的方式,也是我认为最好的方式,是将数组作为对切片的引用(&[u8]&mut [u8])。这与在 C 中获取指向值的指针不同,Rust 中的切片也带有自身的长度,因此您可以安全地遍历它们而不必担心缓冲区溢出或读取所有数据。这确实需要更改下面的算法以考虑可变大小的输入,但大多数时候有一个同样好的选择可供使用。

安全的方法

可以使用 TryInto 将切片转换为数组,但这是以运行时大小检查为代价的,您似乎希望避免这种情况。不过这是一个选项,对性能的影响可能很小。

示例:

fn f(arr: [u8;4]){
  arr[0];
}

fn basic(){
  let mut arr:[u8;12] = [0;12];
  f(arr[8..12].try_into().unwrap());
}

不安全的方式

如果您愿意离开安全地带,您可以做很多事情来强制编译器按照您的意愿识别数据,但它们可能会被滥用。通常最好使用 rust 习语而不是强制使用其他方法,但这是一个有效的选择。

fn basic(){
  let mut arr:[u8;12] = [0;12];
  f(unsafe {*(arr[8..12].as_ptr() as *const [u8; 4])});
}

TL;DR

我建议更改您的类型以使用切片而不是数组,但如果这不可行,我建议您避免不安全,性能不会像您想象的那么糟糕。

使用具有 slice_patterns 功能的 let 绑定。它在 Rust 1.42 中得到稳定。

let v = [1, 2, 3]; // inferred [i32; 3]
let [_, ref subarray @ ..] = v; // subarray is &[i32; 2]
let a = v[0]; // 1
let b = subarray[1]; // 3

Here 是 Rust 参考中关于切片模式的部分。