Heinzi
Heinzi

Reputation: 6083

Create array reference to sub slice

I have a slice of data and want to create an array reference for a fixed-size subslice:

let slice: &[u8] = &[1, 2, 3, 4, 5];
let array_ref: &[u8; 2] = &slice[..2];

Unfortunately, this doesn't work because the type of &[..2] is &[u8] instead of &[u8; 2].

I know about <&[u8; 2]>::try_from(slice), but that incurs a runtime check where I would prefer to use an API that doesn't do this runtime check since I know at runtime that the size requirement is fulfilled.

Is there any API that allows this?

Upvotes: 2

Views: 1425

Answers (1)

Cerberus
Cerberus

Reputation: 10218

In this case, when the slice index is available at compile-time, try_from will be optimized out. We can check this directly in the playground:

pub fn split_two(slice: &[u8]) -> &[u8; 2] {
    slice[..2].try_into().unwrap()
}

Corresponding assembly:

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"

As you can see, the only check here is the bounds check, i.e. the slice is checked to be at least two elements long; but there's no check for the subslice being two elements long, since that's already guaranteed.

If compiler can see that the slice length is sufficient, the first check would be optimized out too; example:

// "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());
    }
}

This is compiled to directly passing the pointer, without any checks at all:

playground::split_two:
    jmpq    *use_ref@GOTPCREL(%rip)

Upvotes: 6

Related Questions