将拥有的数组分成拥有的两半

Mat*_*mas 8 arrays rust

我想将一个拥有的数组分成两个拥有的一半\xe2\x80\x94两个单独的数组,而不是原始数组的切片。各自的大小是编译时间常数。有没有办法在不复制/克隆元素的情况下做到这一点?

\n
let array: [u8; 4] = [0, 1, 2, 3];\n\nlet chunk_0: [u8; 2] = ???;\nlet chunk_1: [u8; 2] = ???;\n\nassert_eq!(\n  [0, 1],\n  chunk_0\n);\nassert_eq!(\n  [2, 3],\n  chunk_1\n);\n
Run Code Online (Sandbox Code Playgroud)\n

因为这相当于仅仅移动元素的所有权,所以我有一种预感,应该对此有一个零成本的抽象。我想知道我是否可以通过巧妙地使用transmute和来做这样的事情forget。但这些功能的文档中有很多可怕的警告。

\n

我的主要动机是在内存中的大型数组上进行操作,而不需要太多的内存副本。例如:

\n
let raw = [0u8; 1024 * 1024];\n\nlet a = u128::from_be_array(???); // Take the first 16 bytes\nlet b = u64::from_le_array(???); // Take the next 8 bytes\nlet c = ...\n
Run Code Online (Sandbox Code Playgroud)\n

我知道实现上述模式的唯一方法是进行大量内存复制,这是多余的。

\n

Net*_*ave 8

您可以使用std::mem:transmute(警告:不安全!):

fn main() {
    let array: [u8; 4] = [0, 1, 2, 3];

    let [chunk_0, chunk_1]: [[u8; 2]; 2] =
        unsafe { std::mem::transmute::<[u8; 4], [[u8; 2]; 2]>(array) };

    assert_eq!([0, 1], chunk_0);
    assert_eq!([2, 3], chunk_1);
}
Run Code Online (Sandbox Code Playgroud)

操场


lko*_*bly 6

use std::convert::TryInto;

let raw = [0u8; 1024 * 1024];
    
let a = u128::from_be_bytes(raw[..16].try_into().unwrap()); // Take the first 16 bytes
let b = u64::from_le_bytes(raw[16..24].try_into().unwrap()); // Take the next 8 bytes
Run Code Online (Sandbox Code Playgroud)

在实践中,我发现编译器在优化方面非常聪明。a通过优化,它将在单个副本中执行上述操作(直接进入分别保存或 的寄存器b)。作为一个例子,根据godbolt,这个:

use std::convert::TryInto;

pub fn cvt(bytes: [u8; 24]) -> (u128, u64) {
    let a = u128::from_be_bytes(bytes[..16].try_into().unwrap()); // Take the first 16 bytes
    let b = u64::from_le_bytes(bytes[16..24].try_into().unwrap()); // Take the next 8 bytes
    (a, b)
}
Run Code Online (Sandbox Code Playgroud)

编译为-C opt-level=3

example::cvt:
        mov     rax, qword ptr [rdi + 8]
        bswap   rax
        mov     rdx, qword ptr [rdi]
        bswap   rdx
        mov     rcx, qword ptr [rdi + 16]
        ret
Run Code Online (Sandbox Code Playgroud)

它优化了任何额外的副本、调用try_into方法、可能的恐慌等等。