我有一个数据切片,想要为固定大小的子切片创建一个数组引用:
let slice: &[u8] = &[1, 2, 3, 4, 5];
let array_ref: &[u8; 2] = &slice[..2];
Run Code Online (Sandbox Code Playgroud)
不幸的是,这不起作用,因为&[..2]is的类型&[u8]而不是&[u8; 2]。
我知道<&[u8; 2]>::try_from(slice),但这会导致运行时检查,我更愿意使用不执行此运行时检查的 API,因为我知道在运行时满足了大小要求。
是否有任何 API 允许这样做?
在这种情况下,当切片索引在编译时可用时,try_from将被优化掉。我们可以直接在Playground中检查这一点:
pub fn split_two(slice: &[u8]) -> &[u8; 2] {
slice[..2].try_into().unwrap()
}
Run Code Online (Sandbox Code Playgroud)
对应的装配:
pub fn split_two(slice: &[u8]) -> &[u8; 2] {
slice[..2].try_into().unwrap()
}
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,这里唯一的检查是边界检查,即检查切片的长度至少为两个元素;但没有检查子切片是否有两个元素长,因为这已经得到保证。
如果编译器可以看到切片长度足够,则第一个检查也会被优化掉;例子:
// "extern" is used as an optimization barrier
extern "Rust" {
fn use_ref(_: &[u8; 2]);
}
pub fn split_two(slice: &[u8; 5]) {
let slice: &[u8] = slice;
// `unsafe` is necessary to call `extern` function (even `extern "Rust"`)
unsafe {
use_ref(slice[..2].try_into().unwrap());
}
}
Run Code Online (Sandbox Code Playgroud)
这被编译为直接传递指针,根本没有任何检查:
playground::split_two:
cmpq $1, %rsi
jbe .LBB0_1
movq %rdi, %rax
retq
.LBB0_1:
pushq %rax
leaq .L__unnamed_1(%rip), %rdx
movl $2, %edi
callq *core::slice::index::slice_end_index_len_fail@GOTPCREL(%rip)
ud2
.L__unnamed_2:
.ascii "src/lib.rs"
.L__unnamed_1:
.quad .L__unnamed_2
.asciz "\n\000\000\000\000\000\000\000\002\000\000\000\005\000\000"
Run Code Online (Sandbox Code Playgroud)