rust-lang / rust-lang/rust

Bounds check may not be properly eliminated with (Bound<usize>, Bound<usize>) indexer but eliminated with some cases

Open
#157,909 4 comments 0 reactions 1 assignee View on GitHub

@saethlin is already working on this.

Since Jun 16, 2026.

A-codegen A-LLVM C-optimization I-slow
Dominant language
Rust
Stars
119k
Forks
16.1k
PR merge metrics
PR metrics pending

Description

Indexing slice with (Bound<usize>, Bound<usize>) may not get optimized property even with inline attribute.
Full compiler explorer: https://godbolt.org/z/96eoPsaTj

Simplest case

pub mod bounds_indexer {
    use std::collections::Bound;

    pub fn test(buf: &[u8]) -> Option<&[u8]> {
        if buf.len() < 4 {
            None
        } else {
            Some(&buf[(Bound::Included(4), Bound::Unbounded)])
        }
    }
}
Original case I initially working on, with impl RangeBounds
pub mod bounds_indexer_with_range_bounds {
    // possible with stable
    use std::ops::RangeBounds;

    pub fn test(buf: &[u8]) -> Option<&[u8]> {
        if buf.len() < 4 { None } else { Some(index(buf, 4..)) }
    }

    //#[inline(always)] // regardless. It looks rustc will inline this function without attribute
    fn index(buf: &[u8], range: impl RangeBounds<usize>) -> &[u8] {
        &buf[(range.start_bound().map(|x| *x), range.end_bound().map(|x| *x))]
    }
}
Other complex cases with several ways to construct (Bound, Bound)
#![cfg_attr(feature="nightly", feature(range_into_bounds))]

#[cfg(feature="nightly")]
pub mod bounds_indexer_with_into_range_bounds {
    // nigltly only requires #![feature(range_into_bounds)]
    use std::ops::IntoBounds;

    pub fn test(buf: &[u8]) -> Option<&[u8]> {
        if buf.len() < 4 { None } else { Some(index(buf, 4..)) }
    }

    //#[inline(always)] // regardless. It looks rustc will inline this function without attribute
    fn index(buf: &[u8], range: impl IntoBounds<usize>) -> &[u8] {
        &buf[range.into_bounds()]
    }
}

// Following is optimized and does not introduce bounds check if none of above two are not compiled.
// If either or both of above cases are compiled, `<(Bound<usize>, Bound<usize>) as SliceIndex<[u8]>>::index`
// will be called and bounds check will be ran in `SliceIndex::index`
pub mod bounds_indexer {
    use std::collections::Bound;

    pub fn test(buf: &[u8]) -> Option<&[u8]> {
        if buf.len() < 4 {
            None
        } else {
            Some(&buf[(Bound::Included(4), Bound::Unbounded)])
        }
    }
}

Following two cases get optimized out.

pub mod bounds_indexer_with_range_bounds_manually_mapped {
    use std::collections::Bound;
    use std::ops::RangeBounds;

    pub fn test(buf: &[u8]) -> Option<&[u8]> {
        if buf.len() < 4 { None } else { Some(index(buf, 4..)) }
    }

    fn index(buf: &[u8], range: impl RangeBounds<usize>) -> &[u8] {
        &buf[match range.start_bound() {
            Bound::Included(&i) => i,
            Bound::Excluded(i) => i.checked_add(1).expect("overflow"),
            Bound::Unbounded => 0,
        }..match range.end_bound() {
            Bound::Included(&i) => i,
            Bound::Excluded(i) => i.checked_sub(1).expect("overflow"),
            Bound::Unbounded => buf.len(),
        }]
    }
}
much shorter case
pub mod raw_index {
    pub fn test(buf: &[u8]) -> Option<&[u8]> {
        if buf.len() < 4 { None } else { Some(&buf[4..]) }
    }
}

Surprisingly, Any of bad case will be optimized if only one of bad cases are compiled.
I think rustc can optimize indexing if there is only one <(Bound<usize>, Bound<usize>) as SliceIndex<[u8]>>::index invocation and can be inlined without almost no code size cost.

What's expected

I expected to see no bounds checks are emitted for all versions

Meta

rustc --version --verbose:
Basically testing with playground and compiler explorer

rustc 1.96.0 (ac68faa20 2026-05-25)
binary: rustc
commit-hash: ac68faa20c58cbccd01ee7208bf3b6e93a7d7f96
commit-date: 2026-05-25
host: x86_64-unknown-linux-gnu
release: 1.96.0
LLVM version: 22.1.2
Compiler returned: 0
Edit hisotry
  • I found the Surprising part is not specific to the implementation, but for any implementation with <(Bound<usize>, Bound<usize>) as SliceIndex<[u8]>>::index so modified accordingly with new fact

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.