我想将一个拥有的数组分成两个拥有的一半\xe2\x80\x94两个单独的数组,而不是原始数组的切片。各自的大小是编译时间常数。有没有办法在不复制/克隆元素的情况下做到这一点?
\nlet 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);\nRun Code Online (Sandbox Code Playgroud)\n因为这相当于仅仅移动元素的所有权,所以我有一种预感,应该对此有一个零成本的抽象。我想知道我是否可以通过巧妙地使用transmute和来做这样的事情forget。但这些功能的文档中有很多可怕的警告。
我的主要动机是在内存中的大型数组上进行操作,而不需要太多的内存副本。例如:
\nlet 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 = ...\nRun Code Online (Sandbox Code Playgroud)\n我知道实现上述模式的唯一方法是进行大量内存复制,这是多余的。
\n您可以使用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)
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方法、可能的恐慌等等。