Sling Academy
Home/Rust/Using drain on a Vec<T> to remove elements while iterating in Rust

Using drain on a Vec to remove elements while iterating in Rust

Last updated: January 04, 2025

Rust's Vec<T> is a commonly used collection that provides powerful features for managing a resizable array of elements. One of the features offered by Vec<T> is the drain method, which enables efficient removal of elements while iterating over them.

Understanding Vec<T> and drain

The Vec<T> type is part of Rust’s standard library and represents a contiguous growable list of elements. It's a highly useful structure when you need an ordered, mutable sequence.

The drain method on a Vec<T> allows for the efficient removal of elements during iteration without needing to use multiple temporary variables or complex logic. The elements removed from the vector will be yielded as an iterator, allowing further operations to be performed on them.

Advantages of Using drain

  • It provides an in-place, efficient way to remove elements inside a loop without extra allocations.
  • The drain method can precisely specify a range within which elements should be removed, which is highly flexible.
  • The elements removed are yielded as an iterator, offering the potential to be collected or further processed.

Basic Usage of drain

Here's a simple example to illustrate how one might use the drain function:

fn main() {
    let mut numbers = vec![1, 2, 3, 4, 5];
    
    for num in numbers.drain(..) {
        println!("Removed number: {}", num);
    }
    
    // After draining: the vector is now empty.
    assert!(numbers.is_empty());
}

In this example, drain is called with a .. range, which covers all elements, effectively clearing the vector by iterating over each element.

Specifying a Range

The drain method allows you to specify a subrange, thus draining only part of the vector:

fn main() {
    let mut numbers = vec![10, 20, 30, 40, 50];
    
    // Draining elements at index 1 and 2 (20, 30)
    for num in numbers.drain(1..3) {
        println!("Drained number: {}", num);
    }
    
    // The vector now holds [10, 40, 50].
    assert_eq!(numbers, vec![10, 40, 50]);
}

Here, only the elements starting at index 1 and up to, but not including, index 3 are drained. This results in both the removal of these elements from numbers and their availability in the loop for other uses.

Using drain for Conditional Removal

The drain method is especially useful when you need to remove elements that meet a specific condition:

fn main() {
    let mut numbers = vec![5, 15, 25, 35, 45];

    // We'll use a temporary vector to hold numbers > 20 for removal
    let to_remove: Vec<_> = numbers.iter().enumerate()
                                   .filter(|&(_, &val)| val > 20)
                                   .map(|(index, _)| index)
                                   .collect();
    
    for index in to_remove.into_iter().rev() {
        numbers.drain(index..index+1);
    }
    
    // The final vector holds numbers <= 20 only
    assert_eq!(numbers, vec![5, 15]);
}

This code uses drain in combination with filter, and enumerate to selectively remove elements from numbers. Note the use of index..index+1 in drain to remove specific single elements.

Conclusion

The drain method in Vec<T> provides a robust way to efficiently remove elements while iterating, without the overhead of additional allocations or complex logic. This allows for more readable and maintainable code whenever in-place modifications are necessary.

By allowing a range, drain supports both precise and bulk removals, illustrating the flexibility of Vec<T> in handling dynamic arrays within Rust programs.

Next Article: Building and merging multiple vectors into one aggregated list in Rust

Previous Article: Rust - Turning vectors into slices with as_slice and as_mut_slice

Series: Collections in Rust

Rust

You May Also Like

  • E0557 in Rust: Feature Has Been Removed or Is Unavailable in the Stable Channel
  • Network Protocol Handling Concurrency in Rust with async/await
  • Using the anyhow and thiserror Crates for Better Rust Error Tests
  • Rust - Investigating partial moves when pattern matching on vector or HashMap elements
  • Rust - Handling nested or hierarchical HashMaps for complex data relationships
  • Rust - Combining multiple HashMaps by merging keys and values
  • Composing Functionality in Rust Through Multiple Trait Bounds
  • E0437 in Rust: Unexpected `#` in macro invocation or attribute
  • Integrating I/O and Networking in Rust’s Async Concurrency
  • E0178 in Rust: Conflicting implementations of the same trait for a type
  • Utilizing a Reactor Pattern in Rust for Event-Driven Architectures
  • Parallelizing CPU-Intensive Work with Rust’s rayon Crate
  • Managing WebSocket Connections in Rust for Real-Time Apps
  • Downloading Files in Rust via HTTP for CLI Tools
  • Mocking Network Calls in Rust Tests with the surf or reqwest Crates
  • Rust - Designing advanced concurrency abstractions using generic channels or locks
  • Managing code expansion in debug builds with heavy usage of generics in Rust
  • Implementing parse-from-string logic for generic numeric types in Rust
  • Rust.- Refining trait bounds at implementation time for more specialized behavior