Bounds check may not be properly eliminated with (Bound<usize>, Bound<usize>) indexer but eliminated with some cases
@saethlin is already working on this.
Since Jun 16, 2026.
- 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]>>::indexso modified accordingly with new fact
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.