创建对子切片的数组引用

Hei*_*nzi 2 arrays slice rust

我有一个数据切片,想要为固定大小的子切片创建一个数组引用:

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 允许这样做?

Cer*_*rus 6

在这种情况下,当切片索引在编译时可用时,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)