Sling Academy
Home/Rust/Rust - Mapping vector elements to new values with map, iter_mut, and collect

Rust - Mapping vector elements to new values with map, iter_mut, and collect

Last updated: January 04, 2025

When working with vectors in Rust, transforming or mapping elements from one value to another is a common task. Rust provides powerful methods to achieve this efficiently, such as map, iter_mut, and collect. Each serves a different purpose and can significantly impact how we manipulate and interact with vectors.

Using map with Iterators

The map function is a method intricately tied to iterators in Rust. It allows you to apply a function directly to elements, producing a new iterator where each element has been transformed accordingly.

fn main() {
    let numbers = vec![1, 2, 3, 4, 5];
    let squared_numbers: Vec<i32> = numbers.iter()
        .map(|&x| x * x)
        .collect();
    println!("{:?}", squared_numbers);
}

In the example above, map processes each element by squaring it. By chaining it with collect, we gather the results into a new vector, squared_numbers.

Modifying Vectors in Place with iter_mut

On many occasions, directly modifying the original vector, instead of creating a transformed copy, can be more efficient. This is where iter_mut comes in handy. It provides mutable references to each element, which allows you to modify elements in place.

fn main() {
    let mut numbers = vec![1, 2, 3, 4, 5];
    numbers.iter_mut().for_each(|x| *x *= 2);
    println!("{:?}", numbers);
}

Here, we double each number directly in the vector using iter_mut. The use of for_each applies the mutation in place, leveraging Rust's capabilities for mutable referencing.

Collecting with collect

While collect is primarily used here to amass results into collections like vectors, it shines in its flexibility. Whether paired with map or other iterator adaptors, collect transforms the nature of resultant data structures effectively.

fn main() {
    let words = vec!["rust", "is", "awesome"];
    let word_lengths: Vec<usize> = words.into_iter()
        .map(|word| word.len())
        .collect();
    println!("{:?}", word_lengths);
}

By converting each string to its length, the above example uses map to transform the vector from &str elements to usize, demonstrating collect's role in changing the types of elements stored in the output vector.

Combining Techniques in Real-World Applications

With basic understandings of map, iter_mut, and collect, you can handle complex transformations and modifications on vectors. Consider this real-world use case of processing and filtering:

fn main() {
    let numbers = vec![1, 2, 3, 4, 5, 6, 7, 8];
    let filtered_squares: Vec<i32> = numbers.into_iter()
        .map(|x| x * x)
        .filter(|&x| x > 15)
        .collect();
    println!("{:?}", filtered_squares);
}

In this scenario, each element is squared, and only values greater than 15 are retained. Such combinations illustrate Rust's powers of chainable iterator combinators and their efficacy in concise, readable operations.

Conclusion

Leveraging map, iter_mut, and collect efficiently streamlines data transformation in Rust. These methods provide clear pathways to make the most out of vector data by elegantly expressing logical transformations and mutations, helping you write expressive, safe, and high-performance code.

Next Article: Rust: Concatenating vectors with append, extend, and the + operator (for strings)

Previous Article: Filtering a Vec in Rust using filter, retain, and drain

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