Vec<T> in Depth

len() and is_empty()

The .len() and .is_empty() methods return the current number of initialized elements in a Vec<T> and whether that count is zero, executing in $O(1)$ time.

Signatures

impl<T> Vec<T> {
    pub const fn len(&self) -> usize;
    pub const fn is_empty(&self) -> bool;
}

Because Vec<T> stores len: usize directly in its 24-byte stack header, both .len() and .is_empty() compile to a single register/memory load and are usable in const contexts.

Element Count vs. Byte Size in Memory

Unlike String::len(), which always returns a byte count (because UTF-8 characters have variable byte lengths), Vec::<T>::len() always returns the number of T elements, regardless of size_of::<T>():

Metric Expression Value for vec![10_u64, 20, 30] (cap = 4)
Initialized element count v.len() 3 elements
Allocated element capacity v.capacity() 4 elements
Initialized byte size on heap std::mem::size_of_val(v.as_slice()) $3 \times 8 = 24$ bytes
Total allocated byte size on heap v.capacity() * size_of::<u64>() $4 \times 8 = 32$ bytes
Stack header size std::mem::size_of_val(&v) 24 bytes
use std::mem::size_of_val;

fn main() {
    let mut v: Vec<u64> = Vec::with_capacity(4);
    println!("Initial: len={}, is_empty={}", v.len(), v.is_empty());

    v.extend([100_u64, 200, 300]);
    println!(
        "Filled:  len={}, is_empty={}, slice_bytes={}",
        v.len(),
        v.is_empty(),
        size_of_val(v.as_slice())
    );
}
Initial: len=0, is_empty=true
Filled:  len=3, is_empty=false, slice_bytes=24

Valid Index Range Invariant (0..v.len())

At all times, only indices in 0..v.len() are initialized and accessible via v[i] or v.get(i). Even when v.capacity() > v.len(), slots in v.len()..v.capacity() are uninitialized memory and cannot be read through safe indexing.