Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.syntblaze.com/llms.txt

Use this file to discover all available pages before exploring further.

The [] (index) operator provides syntactic sugar for accessing elements or sub-slices of a collection using an index or a range. In Rust, this operator evaluates to a place expression, enforces strict runtime bounds checking, and is statically resolved through the type system via the std::ops::Index and std::ops::IndexMut traits.

Desugaring and Trait Mechanics

When the compiler encounters the [] operator, it desugars the syntax into method calls on the underlying traits. For immutable access, container[index] desugars to *container.index(index) via the Index trait:
pub trait Index<Idx> {
    type Output: ?Sized;
    fn index(&self, index: Idx) -> &Self::Output;
}
For mutable access, container[index] = value desugars to *container.index_mut(index) = value via the IndexMut trait:
pub trait IndexMut<Idx>: Index<Idx> {
    fn index_mut(&mut self, index: Idx) -> &mut Self::Output;
}
Notice that the generic parameter Idx is implicitly Sized because the index and index_mut methods take the index argument by value. The associated type Output, however, has a ?Sized bound, allowing the operator to return dynamically sized types like slices ([T]) or string slices (str). Because the trait methods return references (&Output or &mut Output), the compiler implicitly dereferences the result (*) so that the [] operation resolves to the actual value as a place expression. This allows the operator to be used on the left side of an assignment.

Syntax and Index Types

The type of the index (Idx) is dictated by the collection’s trait implementations. For standard contiguous memory collections (arrays, Vec<T>, slices), the index type must implement the std::slice::SliceIndex<[T]> sealed trait. This trait is implemented for usize and various range types, enabling both single-element access and slicing.
let collection = [10, 20, 30, 40, 50];

// Single element access (Idx = usize)
let val = collection[0]; 

// Range access / Slicing (Idx = std::ops::Range<usize>)
let slice = &collection[1..4]; 

// Full range access (Idx = std::ops::RangeFull)
let full_slice = &collection[..]; 
Attempting to use non-usize integer types (such as i32 or u64) will result in a compile-time E0277 error. In Rust, usize and u64 are strictly distinct types in the type system, regardless of the target architecture’s pointer width. Standard collections only implement Index<usize>, so attempting to index with a u64 will fail on all architectures (including 64-bit). Furthermore, because SliceIndex is a sealed trait, implementing RangeBounds on a custom type does not automatically make it usable with the [] operator on standard slices.

Memory and Safety Semantics

Bounds Checking: Unlike C or C++, Rust’s [] operator guarantees memory safety by injecting runtime bounds checks. If the evaluated index is out of bounds for the collection’s length, the thread will immediately panic!. Borrow Checking: Because [] relies on &self and &mut self, standard borrowing rules apply. You cannot hold an immutable reference to an element via &collection[index] while simultaneously mutating the collection, as the index operator borrows the entire collection, not just the specific element. Strings: The [] operator cannot be used with integer indices on String or &str types (e.g., my_string[0]). Because Rust strings are UTF-8 encoded, a single character may span multiple bytes, making O(1) byte-indexing unsafe for character retrieval. However, the [] operator is implemented for strings when using ranges (my_string[0..3]), which slices the underlying bytes. This operation will panic at runtime if the range boundaries do not align with valid UTF-8 character boundaries.
Master Rust with Deep Grasping Methodology!Learn More