Question
I need to iterate over a Vec and access both the current element and its position in the vector. I assume Rust already provides this in the standard API, but I have not found it.
I want something like this:
fn main() {
let v = vec![1; 10];
for (pos, e) in v.iter() {
// do something here
}
}
How can I iterate through a Vec<T> while also getting the index of each element?
Short Answer
By the end of this page, you will understand how to iterate over a Vec<T> in Rust while also getting each element's index. You will learn to use .enumerate(), see how it works with .iter(), .iter_mut(), and .into_iter(), and understand common mistakes beginners make when working with indexed iteration.
Concept
In Rust, iterating over a collection and also tracking the position of each item is a very common task. The standard way to do this is with the .enumerate() adapter.
enumerate() takes any iterator and transforms it into a new iterator that yields pairs:
- the current index
- the current item
For a Vec<T>, you usually start with one of these iterator methods:
.iter()gives immutable references like&T.iter_mut()gives mutable references like&mut T.into_iter()gives owned values likeT
Then you add .enumerate() to include the index.
Example:
let v = vec![10, 20, 30];
for (index, value) in v.iter().enumerate() {
println!("{} -> {}", index, value);
}
This matters because many real programs need both the data and its position, for example:
- showing numbered lists in a UI
- updating items by position
- validating rows in imported data
- formatting output with line numbers
- comparing an element with its index
Instead of manually tracking a counter, Rust encourages using iterator adapters like enumerate() because they are clear, safe, and idiomatic.
Mental Model
Think of .iter() as a person handing you each item from a row of boxes.
Normally, you only get the item:
- box content:
10 - box content:
20 - box content:
30
When you add .enumerate(), that person also tells you which box number it came from:
- box
0:10 - box
1:20 - box
2:30
So enumerate() is like attaching a label number to every item as it comes through the iterator.
It does not change the vector itself. It just changes what the iterator produces.
Syntax and Examples
The most common syntax is:
for (index, item) in vec.iter().enumerate() {
// use index and item
}
Basic example
fn main() {
let v = vec![1, 2, 3];
for (pos, e) in v.iter().enumerate() {
println!("position: {}, value: {}", pos, e);
}
}
Output:
position: 0, value: 1
position: 1, value: 2
position: 2, value: 3
Here:
v.iter()produces&i32.enumerate()turns that into(usize, &i32)posis the indexeis a reference to the element
Modifying values with iter_mut()
fn main() {
let mut v = vec![10, 20, 30];
for (pos, value) in v.iter_mut().enumerate() {
*value += pos as i32;
}
println!("{:?}", v);
}
Output:
[10, 21, 32]
Here:
iter_mut()gives mutable referencesvalueis&mut i32*valueis used to change the actual vector element
Taking ownership with into_iter()
fn main() {
let v = vec!["a", "b", "c"];
for (pos, value) in v.into_iter().enumerate() {
println!("{}: {}", pos, value);
}
}
This consumes v, so you cannot use v afterward.
Direct answer to the question
fn main() {
let v = vec![1; 10];
for (pos, e) in v.iter().enumerate() {
println!("{} -> {}", pos, e);
}
}
Step by Step Execution
Consider this example:
fn main() {
let v = vec![5, 6, 7];
for (pos, e) in v.iter().enumerate() {
println!("pos = {}, e = {}", pos, e);
}
}
Let’s trace it.
Step 1: Create the vector
let v = vec![5, 6, 7];
v is a Vec<i32> containing three elements.
Step 2: Call iter()
v.iter()
This creates an iterator over references:
&5&6&7
Step 3: Call enumerate()
v.iter().enumerate()
Now each yielded value becomes a pair:
(0, &5)(1, &6)(2, &7)
The index type is usize.
Step 4: Pattern matching in the for loop
for (pos, e) in ...
Each pair is unpacked into:
posgets0, then1, then2egets&5, then&6, then&7
Step 5: Print each pair
First iteration:
pos = 0, e = 5
Second iteration:
pos = 1, e = 6
Third iteration:
pos = 2, e = 7
Important detail
e is a reference because iter() was used. Rust lets println! display it naturally, but if you need the value itself in some contexts, remember you may be working with &T rather than T.
Real World Use Cases
Indexed iteration appears in many practical Rust programs.
Display numbered results
let tasks = vec!["build", "test", "deploy"];
for (i, task) in tasks.iter().enumerate() {
println!("{}. {}", i + 1, task);
}
Useful for:
- CLI menus
- todo lists
- ranked output
Validate imported rows
let rows = vec!["Alice", "", "Charlie"];
for (i, row) in rows.iter().enumerate() {
if row.is_empty() {
println!("Row {} is empty", i);
}
}
Useful for:
- CSV import tools
- parsers
- batch processing scripts
Update elements based on position
let mut data = vec![0, 0, 0, 0];
for (i, value) in data.iter_mut().enumerate() {
*value = i as i32;
}
Useful for:
- initializing arrays
- assigning sequence numbers
- generating test data
Format logs with line numbers
let lines = vec!["start", "load config", "done"];
for (i, line) in lines.iter().enumerate() {
println!("[{}] {}", i, line);
}
Useful for:
- debugging
- reporting
- text processing
Real Codebase Usage
In real Rust codebases, developers often combine enumerate() with other iterator tools instead of writing manual counters.
Common pattern: immutable read with index
for (i, item) in items.iter().enumerate() {
println!("{}: {:?}", i, item);
}
Used for reporting, debugging, and rendering.
Common pattern: mutate each element with its index
for (i, item) in items.iter_mut().enumerate() {
*item += i as i32;
}
Used in data transformation and preprocessing.
Common pattern: skip or filter while keeping original positions
for (i, item) in items.iter().enumerate() {
if item.is_empty() {
continue;
}
println!("{} => {}", i, item);
}
This is useful when indexes matter for error messages or traceability.
Guard clauses and validation
for (i, value) in inputs.iter().enumerate() {
if *value < 0 {
println!("Invalid value at index {}", i);
break;
}
}
This pattern appears in validation code, parsers, and API input checking.
Iterator pipelines
enumerate() can also be part of a chain:
let even_positions: Vec<_> = v
.iter()
.enumerate()
.filter(|(i, _)| i % 2 == 0)
.map(|(_, value)| *value)
.collect();
This style is common in production Rust because it is expressive and avoids extra state variables.
Common Mistakes
1. Forgetting to call enumerate()
Broken code:
fn main() {
let v = vec![1, 2, 3];
for (pos, e) in v.iter() {
println!("{} {}", pos, e);
}
}
Why it fails:
v.iter()yields only one thing each time:&i32- the loop expects a pair like
(pos, e)
Fix:
for (pos, e) in v.iter().enumerate() {
println!("{} {}", pos, e);
}
2. Confusing references with values
With .iter(), the item is a reference.
let v = vec![10, 20, 30];
for (i, value) in v.iter().enumerate() {
println!("{} -> {}", i, value);
}
This works fine for printing, but remember value is &i32, not i32.
If you need an owned value for a Copy type:
for (i, value) in v.iter().enumerate() {
let n = *value;
println!("{} -> {}", i, n);
}
3. Using into_iter() and then trying to use the vector again
Broken code:
fn main() {
let v = vec![1, 2, 3];
for (i, value) in v.into_iter().enumerate() {
println!("{} -> {}", i, value);
}
println!("{:?}", v);
}
Why it fails:
into_iter()consumes the vector- ownership moves into the loop
Fix: use .iter() if you still need the vector later.
for (i, value) in v.iter().enumerate() {
println!("{} -> {}", i, value);
}
println!("{:?}", v);
4. Forgetting to dereference with iter_mut()
Broken code:
let mut v = vec![1, 2, 3];
for (i, value) in v.iter_mut().enumerate() {
value += i as i32;
}
Why it fails:
valueis&mut i32- you must modify the pointed-to value
Fix:
for (i, value) in v.iter_mut().enumerate() {
*value += i as i32;
}
5. Assuming indexes start at 1
enumerate() starts at 0, not 1.
If you want human-friendly numbering:
for (i, item) in v.iter().enumerate() {
println!("Item {}: {}", i + 1, item);
}
Comparisons
| Approach | What it gives you | Consumes vector? | Common use |
|---|---|---|---|
v.iter() | &T | No | Read items without index |
v.iter().enumerate() | (usize, &T) | No | Read items with index |
v.iter_mut().enumerate() | (usize, &mut T) | No | Modify items with index |
v.into_iter().enumerate() | (usize, T) | Yes | Take ownership with index |
enumerate() vs manual counter
Manual counter
let mut i = 0;
for value in v.iter() {
println!("{} -> {}", i, value);
i += 1;
}
Idiomatic Rust
for (i, value) in v.iter().enumerate() {
println!("{} -> {}", i, value);
}
Why enumerate() is better:
- shorter
- clearer
- less error-prone
- avoids managing extra mutable state
enumerate() vs indexing with a range
Using a range
for i in 0..v.len() {
println!("{} -> {}", i, v[i]);
}
Using enumerate()
for (i, value) in v.iter().enumerate() {
println!("{} -> {}", i, value);
}
enumerate() is often preferred because:
- it works directly with iterators
- it avoids repeated indexing
- it is safer and more idiomatic
- it fits well with iterator chains
Cheat Sheet
Indexed iteration in Rust
Read-only iteration
for (i, item) in v.iter().enumerate() {
println!("{} -> {}", i, item);
}
iisusizeitemis&T
Mutable iteration
for (i, item) in v.iter_mut().enumerate() {
*item = i as i32;
}
itemis&mut T- use
*itemto modify the value
Owning iteration
for (i, item) in v.into_iter().enumerate() {
println!("{} -> {:?}", i, item);
}
itemisTvis consumed
Key rule
enumerate() turns:
Iterator<Item = X>
into:
Iterator<Item = (usize, X)>
Common fix for the original problem
for (pos, e) in v.iter().enumerate() {
// use pos and e
}
Watch out for
enumerate()starts at0.iter()gives references.into_iter()moves ownership.iter_mut()requires dereferencing with*item
FAQ
How do I get the index while iterating over a vector in Rust?
Use .enumerate() after creating an iterator:
for (i, item) in v.iter().enumerate() {
// ...
}
Does enumerate() work only with Vec?
No. It works with any iterator, including iterators from slices, arrays, strings, and custom iterator types.
Why is the index type usize?
Because indexes in Rust collections use usize, which is the platform-sized unsigned integer type used for memory indexing.
What is the difference between iter() and into_iter() with enumerate()?
iter()gives references like&Tinto_iter()gives owned values likeTand consumes the collection
Can I start counting from 1 instead of 0?
enumerate() always starts at 0. If you want display numbering from 1, use i + 1 when printing or formatting.
Can I modify elements while also using the index?
Yes. Use .iter_mut().enumerate().
for (i, item) in v.iter_mut().enumerate() {
*item += i as i32;
}
Is using enumerate() better than indexing with 0..v.len()?
Usually yes. It is more idiomatic, works naturally with iterators, and avoids manual indexing in many cases.
Mini Project
Description
Build a small Rust program that prints a numbered shopping list and marks empty entries as invalid. This demonstrates how to use .enumerate() in a realistic task where both the item and its position matter.
Goal
Create a program that loops through a vector, prints each item with a human-friendly number, and reports invalid entries using their index.
Requirements
- Create a
Vecof shopping list items, including at least one empty string. - Iterate through the vector using indexed iteration.
- Print valid items as a numbered list starting from 1.
- Detect empty items and print an error message with their zero-based index.
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.