Question
How can I apply a mapping function to every element of a Rust Vec?
This custom function creates a new vector by applying a function to each item:
fn map<A, B>(items: &[A], f: impl Fn(&A) -> B) -> Vec<B> {
let mut result = Vec::with_capacity(items.len());
for item in items {
result.push(f(item));
}
result
}
fn add_one(value: &i32) -> i32 {
*value + 1
}
fn main() {
let values = vec![1, 2, 3];
let mapped = map(&values, add_one);
println!("{:?}", mapped);
}
Why does Vec not provide a built-in map method? Is there an idiomatic standard-library approach that also works with collections such as LinkedList?
Short Answer
Rust maps values through the Iterator trait rather than through a Vec-specific method. You will learn to call iter().map(...) to describe a transformation and collect() to build the resulting collection, usually a Vec.
Concept
map transforms each item in a sequence into a new value. For example, mapping [1, 2, 3] with “add one” produces [2, 3, 4].
In Rust, map is defined by the Iterator trait. A Vec is a collection that can produce an iterator; it is not itself where transformation behavior needs to live.
let values = vec![1, 2, 3];
let incremented: Vec<i32> = values.iter().map(|value| value + 1).collect();
assert_eq!(incremented, vec![2, 3, 4]);
This design matters because the same iterator methods work for many sources of values:
Vec<T>and slicesLinkedList<T>- arrays
- ranges such as
1..10 - hash-map keys, values, or entries
- file lines and other streaming input
map is also lazy. Calling it creates an iterator recipe; it does not immediately create a new vector. A consuming operation such as collect, for, sum, or find makes the iterator do its work.
Vectors are not only for procedural programming. They are general-purpose collections, and Rust supports both explicit for loops and iterator pipelines.
Mental Model
Think of an iterator as a conveyor belt.
- A
Vecis a box containing items. iter()puts the items onto a conveyor belt as references.map()installs a machine that changes each item as it passes.collect()puts the transformed items into a new box.
The transformation machine is not built into every possible box type. Instead, it is attached to the shared conveyor-belt interface: Iterator. That is why both vectors and linked lists can use map.
Syntax and Examples
The common pattern is:
let output: Vec<OutputType> = input
.iter()
.map(|item| /* create an OutputType */)
.collect();
Map borrowed values
Use .iter() when you want to keep the original vector. The closure receives &T.
fn main() {
let names = vec!["ada", "grace", "linus"];
let uppercased: Vec<String> = names
.iter()
.map(|name| name.to_uppercase())
.collect();
assert_eq!(uppercased, vec!["ADA", "GRACE", "LINUS"]);
assert_eq!(names[0], "ada");
}
to_uppercase() creates a new String, so the original names vector remains available.
Map owned values
Use .into_iter() when the input vector is no longer needed and the mapping operation needs ownership of each value.
fn main() {
let names = vec![String::from("Ada"), String::from("Grace")];
let lengths: Vec<usize> = names
.into_iter()
.map(|name| name.len())
.collect();
assert_eq!(lengths, vec![3, 5]);
}
After into_iter(), names has been moved and cannot be used.
Use a named function
A closure is convenient, but a function works too when its argument type matches.
fn add_one(value: &i32) -> i32 {
value + 1
}
fn main() {
let values = vec![1, 2, 3];
let mapped: Vec<i32> = values.iter().map(add_one).collect();
assert_eq!(mapped, vec![2, 3, 4]);
}
Step by Step Execution
Consider this code:
let values = vec![1, 2, 3];
let doubled: Vec<i32> = values.iter().map(|value| value * 2).collect();
Execution proceeds as follows:
valuesowns the integers[1, 2, 3].values.iter()creates an iterator that yields&i32: first&1, then&2, then&3..map(|value| value * 2)creates a new iterator. It records the transformation but does not yet visit every item..collect()asks for all mapped values and constructs the requested result type,Vec<i32>.- The closure runs once for each reference:
&1becomes2&2becomes4&3becomes6
doubledbecomesvec![2, 4, 6].
Because i32 is Copy, Rust automatically reads its value when evaluating value * 2. For non-Copy values, you may need to clone, borrow, or consume values deliberately.
Real World Use Cases
map is useful whenever every input item should become exactly one output item.
- API responses: convert database records into public response objects.
- Display data: convert prices in cents into formatted strings.
- Parsing: turn input strings into normalized values.
- Configuration: transform environment-variable strings into application settings.
- Geometry and graphics: convert points from one coordinate system to another.
- Data processing: derive a field, such as a list of user IDs, from a list of users.
struct User {
id: u64,
name: String,
}
fn main() {
let users = vec![
User { id: 10, name: String::from("Ada") },
User { id: 20, name: String::from("Grace") },
];
let ids: Vec<u64> = users.iter().map(|user| user.id).collect();
assert_eq!(ids, vec![10, 20]);
}
Real Codebase Usage
In real Rust projects, iterator chains are often used to express a data flow clearly.
Transform after validation
Use a guard clause for invalid input, then map valid values.
fn normalized_tags(tags: &[String]) -> Vec<String> {
tags.iter()
.filter(|tag| !tag.trim().is_empty())
.map(|tag| tag.trim().to_lowercase())
.collect()
}
Return errors while transforming
When mapping can fail, collect into a Result.
fn parse_ports(inputs: &[String]) -> Result<Vec<u16>, std::num::ParseIntError> {
inputs.iter()
.map(|text| text.parse::<u16>())
.collect()
}
collect() turns an iterator of Result<u16, _> into Result<Vec<u16>, _>. It returns the first error, or a vector when all parsing succeeds.
Write reusable functions for iterables
If a function only needs to read values, accept a slice when appropriate:
fn square_all(values: &[i32]) -> Vec<i32> {
values.iter().map(|value| value * value).collect()
}
Using &[i32] lets callers pass a Vec<i32>, an array slice, or another slice without requiring a particular collection type.
Common Mistakes
Expecting map to create a vector immediately
This does not produce a Vec; it produces an iterator:
let values = vec![1, 2, 3];
let mapped = values.iter().map(|value| value + 1);
Fix it by consuming the iterator:
let mapped: Vec<i32> = values.iter().map(|value| value + 1).collect();
Forgetting the result type for collect()
Rust may not know which collection to build.
let result = (1..4).map(|value| value * 2).collect(); // type may be ambiguous
Provide a type annotation or a turbofish:
let result: Vec<i32> = (1..4).map(|value| value * 2).collect();
let also_result = (1..4).map(|value| value * 2).collect::<Vec<i32>>();
Moving a vector accidentally with into_iter()
let names = vec![String::from("Ada")];
let lengths: Vec<usize> = names.into_iter().map(|name| name.len()).collect();
println!("{:?}", names); // error: names was moved
Use .iter() if you need names afterward, or intentionally use .into_iter() when consuming it is correct.
Using map when items should be removed
map always produces one output for each input. To discard items, use filter first:
let numbers = vec![1, 2, 3, 4];
let even_squares: Vec<i32> = numbers
.iter()
.filter(|number| *number % 2 == 0)
.map(|number| number * number)
.collect();
Writing &Vec<T> when a slice is enough
Prefer &[T] for a read-only function parameter:
fn good(values: &[i32]) -> Vec<i32> {
values.iter().map(|value| value + 1).collect()
}
A slice is more flexible and still supports iteration.
Comparisons
| Approach | Best when | Result behavior |
|---|---|---|
for loop with push | The loop has multiple statements, mutation, or complex control flow | You explicitly build the result vector |
.iter().map(...).collect() | Every input maps to one output and the transformation is clear | Declarative, lazy until collected |
.iter().filter(...).collect() | You only want to keep certain items | May produce fewer items |
.iter().filter(...).map(...).collect() | You need to select items and then transform them | Filters first, then transforms survivors |
.into_iter().map(...).collect() | The source collection can be consumed | The closure receives owned items |
.iter_mut().map(...) | You need mutable references to elements | Usually paired with for_each; often a for loop is clearer |
Vec does not need its own map because its iterator already implements the shared Iterator::map method. LinkedList follows the same approach: call .iter().map(...) and collect into the collection type you want.
use std::collections::LinkedList;
let list: LinkedList<i32> = [1, 2, 3].into_iter().collect();
let mapped: LinkedList<i32> = list.iter().map(|value| value + 1).collect();
assert_eq!(mapped.into_iter().collect::<Vec<_>>(), vec![2, 3, 4]);
Cheat Sheet
// Borrow elements; keep the original Vec
let output: Vec<B> = input.iter().map(|item: &A| /* B */).collect();
// Consume the Vec; closure receives A
let output: Vec<B> = input.into_iter().map(|item: A| /* B */).collect();
// Transform and keep only selected values
let output: Vec<B> = input
.iter()
.filter(|item| /* bool */)
.map(|item| /* B */)
.collect();
// A fallible transformation
let output: Result<Vec<B>, E> = input.iter().map(|item| /* Result<B, E> */).collect();
mapbelongs toIterator, so it works across collection types.mapis lazy; usecollect,for,sum,count, and similar consumers to run it.- Annotate
collect()when Rust cannot infer the output collection type. - Use
.iter()to borrow,.into_iter()to consume, and.iter_mut()to mutably borrow. - Use
&[T], not&Vec<T>, for functions that only need a read-only sequence. - Use
filterto remove items;mapalone preserves the number of items.
FAQ
Does Rust Vec have a map method?
No. The standard approach is vec.iter().map(...).collect::<Vec<_>>(). map is an Iterator method so it can be shared by many iterable types.
Why is map lazy in Rust?
Laziness lets Rust combine operations such as filter, map, and take without allocating an intermediate collection for each step. Work begins when a consumer such as collect() requests values.
Should I use iter() or into_iter() before map?
Use iter() when you want to keep the original collection and can work with references. Use into_iter() when you no longer need it or need to move each item into the transformation.
How do I map a vector into another Vec type?
Specify the target type:
let lengths: Vec<usize> = words.iter().map(|word| word.len()).collect();
Can I use map on a LinkedList in Rust?
Yes. Call list.iter().map(...) or list.into_iter().map(...), then use collect() to choose the output collection.
When is a for loop better than map?
Use a for loop when each iteration performs several actions, needs break or continue, or mutates state in a way that makes an iterator chain harder to read.
Can map handle errors?
Yes. Return Result from the closure and collect into Result<Vec<T>, E>. Rust will return an error if any mapped operation fails.
Mini Project
Description
Build a small order-summary transformation. An application often receives raw item names and prices in cents, then needs a display-ready list without changing the original input. This project practices mapping borrowed vector items into a new type and collecting the output.
Goal
Create a function that converts order items into formatted receipt lines and returns them as a Vec<String>.
Requirements
Use a struct to represent an item with a name and a price in cents.
Create a function that accepts a slice of items.
Use iter, map, and collect rather than manually pushing into a vector.
Format each output line as name: $dollars.cents.
Keep the original items available after creating the receipt lines.
Keep learning
Related questions
Absolute Values in Rust: Using the abs() Method
Learn how to calculate absolute values in modern Rust with abs(), handle signed integer overflow, and choose safer alternatives.
Accepting Iterators in Rust Functions with impl Iterator
Learn how to write Rust functions that accept HashMap iterators using generic bounds or impl Iterator, avoiding trait object and lifetime errors.
Access Enum Variant Values in Rust with Pattern Matching
Learn how to extract and print values stored in Rust enum variants using match and if let pattern matching, with practical examples.