Idiom or utility for 'slice within bounds'

Given a pair of indices lo and hi I would like to take a slice between those limits, but if hi is out of bounds, then automatically limit it to stay within bounds. This is easily done like this

&v[lo..hi.min(v.len())]

but I was wondering whether there is a more concise or higher-level way of writing this, whether by idiom or through the use of some utility.

The only other option I could think of is this:

v.get(lo..hi).unwrap_or(&v[lo..])

(Playground)

It's longer, but maybe a bit easier to read?


Note that both your original approach as well as the approach above panic if lo is exceeding the boundary. Not sure if that's what you intended.

Interesting that unwrap_or() variant gives better assembly and completely avoids panic somehow. Rust Playground

1 Like

If you want to automatically limit lo as well, you could do this:

pub fn foo<T>(v: &[T], lo: usize, hi: usize) -> &[T] {
    let m = v.len();
    &v[lo.min(m)..hi.min(m)]
}

pub fn bar<T>(v: &[T], lo: usize, hi: usize) -> &[T] {
    v.get(lo..hi).or(v.get(lo..)).unwrap_or(&[])
}

(Playground)

Not sure if that could be written nicer yet.

Does it?

I'm no assembly maven, so it's not at all clear to me when looking at this godbolt output.

Your playground link appears to suggest the opposite: both versions panic.

I get this on playground:

playground::foo:
	cmpq	$4, %rdi
	movl	$4, %eax
	cmovbq	%rdi, %rax
	testq	%rax, %rax
	je	.LBB7_1
	decq	%rax
	retq

.LBB7_1:
	pushq	%rax
	leaq	.L__unnamed_7(%rip), %rdx
	movl	$1, %edi
	xorl	%esi, %esi
	callq	*core::slice::index::slice_index_order_fail@GOTPCREL(%rip)
	ud2

playground::bar:
	leaq	-1(%rdi), %rax
	cmpq	$5, %rdi
	movl	$3, %edx
	cmovbq	%rax, %rdx
	leaq	.L__unnamed_8+4(%rip), %rax
	retq

And this following your godbolt link:

example::aaa:
        cmp     rcx, rsi
        cmovb   rsi, rcx
        mov     rcx, rsi
        sub     rcx, rdx
        jb      .LBB0_1
        lea     rax, [rdi + 4*rdx]
        mov     rdx, rcx
        ret
.LBB0_1:
        push    rax
        lea     rax, [rip + .L__unnamed_1]
        mov     rdi, rdx
        mov     rdx, rax
        call    qword ptr [rip + core::slice::index::slice_index_order_fail@GOTPCREL]
        ud2

example::bbb:
        mov     r9, rsi
        sub     r9, rdx
        jb      .LBB1_1
        mov     r8, rcx
        sub     r8, rdx
        cmp     rcx, rsi
        cmova   r8, r9
        cmp     rcx, rdx
        lea     rax, [rdi + 4*rdx]
        cmovb   r8, r9
        mov     rdx, r8
        ret
.LBB1_1:
        push    rax
        lea     rax, [rip + .L__unnamed_2]
        mov     rdi, rdx
        mov     rdx, rax
        call    qword ptr [rip + core::slice::index::slice_start_index_len_fail@GOTPCREL]
        ud2

Hmm, I couldn't immediately find how to generate ASM on playground and thought I'd imagined that the possibility existed ...

OK, so the playground gives much cleaner code than godbolt (even on the highest OPT level).

Moral of the story: look at Rust-generated ASM on playground, not godbolt?

(But both approaches panic, or have I missed something there too?)

Yes, bar should panic (and does panic), but I don't see how the assembler output from Playground can panic. Maybe someone with more assembly experience could check on that.

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.