Syntax Cache
BlogMethodFeaturesHow It WorksBuild a Game
  1. Home
  2. Rust
  3. Rust Collections & Iterators Practice: Vec, HashMap, Iterator Adapters
Rust59 exercises

Rust Collections & Iterators Practice: Vec, HashMap, Iterator Adapters

Master Rust collections and iterator patterns. Practice Vec, HashMap, iterator adapters, and the difference between iter(), into_iter(), and iter_mut().

Common ErrorsQuick ReferencePractice
Warm-up1 / 2

Can you write this from memory?

Create an empty Vec of i32 values named `numbers`.

On this page
  1. 1Compiler Error E0282: Type Annotations Needed on collect()
  2. 2Compiler Error E0277: Cannot Build Vec<T> from &T
  3. 3copied() vs cloned()
  4. 4iter() vs into_iter() vs iter_mut(): The Ownership Decision
  5. 5Compiler Error E0507: Cannot Move Out of Borrowed Content
  6. 6Chaining Adapters: map, filter, and Friends
  7. 7HashMap Patterns
  8. Entry API Cookbook
  9. HashMap Iteration Order
  10. 8Edition Note: array.into_iter() Changed in Rust 2021
  11. 9Further Reading
Compiler Error E0282: Type Annotations Needed on collect()Compiler Error E0277: Cannot Build Vec<T> from &Tcopied() vs cloned()iter() vs into_iter() vs iter_mut(): The Ownership DecisionCompiler Error E0507: Cannot Move Out of Borrowed ContentChaining Adapters: map, filter, and FriendsHashMap PatternsEdition Note: array.into_iter() Changed in Rust 2021Further Reading

Rust's standard library provides powerful collections: Vec for dynamic arrays, HashMap for key-value storage, and more. But the real power comes from iterators. They're lazy, composable transformations that chain together elegantly.

The iterator ecosystem can be confusing at first. When do you use iter() vs into_iter()? Why does collect() need a type annotation? How do you chain map, filter, and fold without getting lost?

This page focuses on collection and iterator patterns until they become natural. You'll practice creating collections, transforming them with iterators, and collecting results.

Related Rust Topics
Rust Traits & Generics: Trait Bounds, impl Trait, TurbofishRust Ownership & Borrowing: Move Semantics & Borrow Rules

collect() can build many collection types (Vec, HashSet, String, HashMap, Result<Vec<_>, _>), so the compiler often cannot infer which one you want:

let v = vec![1, 2, 3];

// WRONG: No type hint
// let result = v.iter().map(|x| x * 2).collect();
// error[E0282]: type annotations needed

// RIGHT: Annotate the variable
let result: Vec<i32> = v.iter().map(|x| x * 2).collect();

// RIGHT: Use turbofish
let result = v.iter().map(|x| x * 2).collect::<Vec<_>>();

Turbofish is useful mid-chain. Type annotation is clearer for standalone bindings. Common collect targets:

// Vec
let v = iter.collect::<Vec<_>>();

// HashMap from tuples
let m = pairs.collect::<HashMap<_, _>>();

// Result — collect stops at first Err
let results = items.map(|x| x.parse::<i32>()).collect::<Result<Vec<_>, _>>();

// String — join chars or &strs
let s = chars.collect::<String>();

Ready to practice?

Start practicing Rust Collections & Iterators: Vec, HashMap, Iterator Adapters with spaced repetition

.iter() yields references. If you collect into a type that expects owned values, the compiler rejects it:

let names = vec![String::from("alice"), String::from("bob")];

// OK: map produces new owned Strings (to_uppercase allocates)
let upper: Vec<String> = names.iter().map(|s| s.to_uppercase()).collect();

// WRONG: collecting &String directly into Vec<String>
// let cloned: Vec<String> = names.iter().collect();
// error[E0277]: a value of type Vec<String> cannot be built from &String

// RIGHT: .cloned() converts &T to T (calls Clone)
let cloned: Vec<String> = names.iter().cloned().collect();

// RIGHT: .into_iter() yields owned values directly
let owned: Vec<String> = names.into_iter().collect();
// names is consumed and can no longer be used

Both convert &T to T, but they work differently:

AdapterRequiresDoes
.copied()T: CopyBitwise copy (no heap allocation)
.cloned()T: CloneCalls .clone() (may allocate)
// copied() — for Copy types (i32, f64, bool, char, etc.)
let nums = vec![1, 2, 3];
let owned: Vec<i32> = nums.iter().copied().collect();

// cloned() — for Clone types that may allocate (String, Vec, etc.)
let names = vec![String::from("alice"), String::from("bob")];
let owned: Vec<String> = names.iter().cloned().collect();

Prefer .copied() when the type is Copy — it documents that the operation is cheap.

Start with .iter() (borrows, collection survives). Use .into_iter() only when you need ownership or won't use the collection again. Use .iter_mut() to modify elements in place.

The first question to answer when iterating:

MethodYieldsCollection after?
.iter()&TStill usable
.iter_mut()&mut TStill usable (mutated)
.into_iter()TConsumed, gone
let mut v = vec![1, 2, 3];

// Borrow: read only, v survives
for x in v.iter() { println!("{x}"); }

// Mutable borrow: modify in place, v survives
for x in v.iter_mut() { *x += 10; }

// Consume: take ownership, v is gone
for x in v.into_iter() { println!("{x}"); }
// v cannot be used after this point

Start with .iter(). Switch to .into_iter() only when you need ownership or won't use the collection again. Use .iter_mut() when modifying elements in place. Understanding these choices requires a solid grasp of ownership and borrowing -- the same rules that govern &T vs &mut T vs T apply to iterator output.

This appears when you try to take ownership through a borrow:

let names = vec![String::from("alice"), String::from("bob")];

// WRONG: iter() yields &String, but you try to move
// for name in names.iter() {
//     let owned: String = *name;  // error[E0507]
// }

// RIGHT: use into_iter() to take ownership
for name in names.into_iter() {
    let owned: String = name;  // fine — already owned
}

// RIGHT: clone if you need the collection afterward
let names = vec![String::from("alice"), String::from("bob")];
for name in names.iter() {
    let owned: String = name.clone();
}

Iterator adapters are lazy—nothing happens until you consume with .collect(), .sum(), or a for loop. Chain freely without intermediate allocations.

Iterator adapters are lazy — nothing happens until you consume the iterator with .collect(), .sum(), .for_each(), or a for loop. Each adapter takes a closure, making closures and iterators inseparable in idiomatic Rust:

let v = vec![1, 2, 3, 4, 5];

// filter + map + collect
let doubled_evens: Vec<i32> = v.iter()
    .filter(|&&x| x % 2 == 0)
    .map(|&x| x * 2)
    .collect();
// [4, 8]

// fold for aggregation
let sum: i32 = v.iter().fold(0, |acc, &x| acc + x);

// filter_map combines filter and map in one step
let parsed: Vec<i32> = ["1", "two", "3"].iter()
    .filter_map(|s| s.parse().ok())
    .collect();
// [1, 3]

Use the Entry API (entry().or_insert()) to avoid double lookups. Use or_insert_with for expensive defaults. HashMap iteration order is non-deterministic—use BTreeMap if you need sorted keys.

use std::collections::HashMap;

let mut scores = HashMap::new();
scores.insert("blue", 10);
scores.insert("red", 25);

// Access with get (returns Option<&V>)
let blue_score = scores.get("blue"); // Some(&10)

// Entry API for conditional insert
scores.entry("green").or_insert(50);

// Build from iterator of tuples
let teams = vec![("blue", 10), ("red", 25)];
let scores: HashMap<&str, i32> = teams.into_iter().collect();

Entry API Cookbook

The Entry API avoids double lookups. Three patterns you'll use constantly:

use std::collections::HashMap;
let mut counts: HashMap<&str, i32> = HashMap::new();
let words = vec!["hello", "world", "hello"];

// Counting: increment or initialize to 0
for word in &words {
    *counts.entry(word).or_insert(0) += 1;
}

// Lazy default: only compute if key is missing
let mut cache: HashMap<String, Vec<u8>> = HashMap::new();
let data = cache.entry("key".into())
    .or_insert_with(|| expensive_computation());

// Update-or-insert: modify existing, insert if absent
let mut scores: HashMap<&str, i32> = HashMap::new();
scores.entry("player")
    .and_modify(|s| *s += 10)
    .or_insert(10);

or_insert_with takes a closure — the value is only computed when the key is missing. Use this instead of or_insert when the default is expensive.

HashMap Iteration Order

HashMap uses randomly seeded hashing for DoS resistance, so iteration order can vary between runs. If you need deterministic order:

  • Sorted keys: Use BTreeMap (keys iterate in sorted order)
  • Insertion order: Use IndexMap from the indexmap crate

In Rust 2018, [1, 2, 3].into_iter() yielded references (&i32) for backwards compatibility. In Rust 2021+, it yields owned values (i32):

// Rust 2021: iterates by value
let arr = [1, 2, 3];
for x in arr.into_iter() {
    // x is i32 (owned)
}

// If you want references explicitly:
for x in arr.iter() {
    // x is &i32
}

If you're reading older code or upgrading editions, watch for this change. cargo fix --edition handles the migration automatically. For a compact reference of all iterator adapters and collection patterns, see the iterators cheat sheet.

  • The Rust Book: Iterators — lazy evaluation, consuming adapters
  • std::iter::Iterator — full API with collect() guidance
  • HashMap docs — entry API, hashing, performance
  • Rust Edition Guide: IntoIterator for Arrays — the 2021 behavior change

When to Use Rust Collections & Iterators: Vec, HashMap, Iterator Adapters

  • Use `Vec` for growable arrays when you do not know the size at compile time.
  • Use `HashMap` for key-value associations with fast lookup.
  • Use iterators to transform collections without mutation.
  • Chain iterator adapters for complex transformations.
  • Use `collect()` to turn an iterator back into a collection.

Check Your Understanding: Rust Collections & Iterators: Vec, HashMap, Iterator Adapters

Prompt

What is the difference between iter(), into_iter(), and iter_mut()?

What a strong answer looks like

`iter()` borrows elements (`&T`). `into_iter()` takes ownership (consumes the collection). `iter_mut()` borrows mutably (`&mut T`). Choose based on whether you need ownership or just want to read/modify.

What You'll Practice: Rust Collections & Iterators: Vec, HashMap, Iterator Adapters

Create and use VecCreate and use HashMapChoose between iter(), into_iter(), iter_mut()Use map to transform elementsUse filter to select elementsChain multiple iterator adaptersCollect iterators into collectionsUse fold and reduce for aggregationUnderstand iterator ownership

Common Rust Collections & Iterators: Vec, HashMap, Iterator Adapters Pitfalls

  • You chain `.map()` and `.filter()` but nothing happens. Iterator adapters are lazy --- they produce no output until consumed. Add `.collect()`, `.sum()`, `.for_each()`, or use a `for` loop to drive the iterator.
  • You get `error[E0382]: borrow of moved value` after a `for` loop. You used `.into_iter()` (or `for x in vec`), which consumes the collection. Switch to `.iter()` or `&vec` if you need the collection afterward.
  • You get `error[E0282]: type annotations needed` on `.collect()`. The compiler cannot infer which collection type to build. Add a turbofish `.collect::<Vec<_>>()` or annotate the binding type.
  • You get `error[E0277]` collecting references into a `Vec<T>`. You called `.iter()` which yields `&T`, but the target collection expects `T`. Insert `.cloned()` or `.copied()` before `.collect()`, or switch to `.into_iter()`.
  • Your HashMap iteration prints keys in a different order every run. HashMap does not guarantee insertion order. Use `BTreeMap` if you need sorted keys, or `.sorted()` from itertools for one-off ordering.
  • You call `.unwrap()` inside a `.map()` and panic on bad data. Use `.filter_map(|x| x.parse().ok())` to silently skip failures, or `.map(...).collect::<Result<Vec<_>, _>>()` to propagate errors.

Rust Collections & Iterators: Vec, HashMap, Iterator Adapters FAQ

Why are iterators lazy?

Iterator adapters like `map` and `filter` don't do anything until you consume the iterator. This allows chaining without intermediate allocations. Call `.collect()`, `.sum()`, or iterate with `for` to actually execute.

Why does collect() need a type annotation?

`collect()` can produce many collection types (Vec, HashSet, String, etc.). The compiler needs to know which one: `.collect::<Vec<_>>()` or `let v: Vec<_> = iter.collect()`.

When should I use fold vs reduce?

`fold` takes an initial value and works on any type: `fold(0, |acc, x| acc + x)`. `reduce` uses the first element as initial and requires the same type. Use `fold` when the accumulator type differs from elements.

How do I iterate over HashMap entries?

`for (key, value) in &map { }` iterates over references. `for (key, value) in map { }` consumes the map. Use `.iter()`, `.keys()`, or `.values()` for specific access patterns.

What is the difference between filter and filter_map?

`filter` keeps elements where the predicate is true. `filter_map` transforms elements and filters simultaneously: `filter_map(|x| x.parse().ok())` parses and keeps only successes.

Rust Collections & Iterators: Vec, HashMap, Iterator Adapters Syntax Quick Reference

Create Vec
let v: Vec<i32> = Vec::new();
let v = vec![1, 2, 3];
Vec operations
v.push(4);
v.pop();
v.len();
Create HashMap
use std::collections::HashMap;
let mut map = HashMap::new();
map.insert("key", "value");
HashMap access
let value = map.get("key");
map.entry("key").or_insert("default");
iter() vs into_iter()
for x in &v { println!("{x}"); }  // borrows: &T
for x in v { println!("{x}"); }   // consumes: T
map and filter
let doubled: Vec<_> = v.iter()
    .filter(|x| *x > 0)
    .map(|x| x * 2)
    .collect();
fold
let sum: i32 = v.iter().fold(0, |acc, x| acc + x);
collect with turbofish
let v: Vec<_> = iter.collect();
let v = iter.collect::<Vec<_>>();
filter_map
let numbers: Vec<i32> = strings.iter()
    .filter_map(|s| s.parse().ok())
    .collect();
enumerate
for (index, value) in v.iter().enumerate() {
    println!("{index}: {value}");
}

Rust Collections & Iterators: Vec, HashMap, Iterator Adapters Sample Exercises

Example 1Difficulty: 1/5

Fill in the method to add an element to the Vec.

push
Example 2Difficulty: 1/5

Fill in the macro to create a Vec literal.

vec!
Example 3Difficulty: 2/5

Fill in the constructor to create an empty HashMap.

new()

+ 56 more exercises

Quick Reference
Rust Collections & Iterators: Vec, HashMap, Iterator Adapters Cheat Sheet →

Copy-ready syntax examples for quick lookup

Further Reading

  • GDScript Dictionary map() and map_in_place12 min read
  • Rust Newtype Pattern: Catch Unit Bugs at Compile Time18 min read

Start practicing Rust Collections & Iterators: Vec, HashMap, Iterator Adapters

Free daily exercises with spaced repetition. No credit card required.

← Back to Rust Syntax Practice
Syntax Cache

Build syntax muscle memory with spaced repetition.

Product

  • Pricing
  • Our Method
  • Daily Practice
  • Design Patterns
  • Interview Prep

Resources

  • Blog
  • Compare
  • Cheat Sheets
  • Vibe Coding
  • Muscle Memory

Languages

  • Python
  • JavaScript
  • TypeScript
  • Rust
  • SQL
  • GDScript

Legal

  • Terms
  • Privacy
  • Contact

© 2026 Syntax Cache

Cancel anytime in 2 clicks. Keep access until the end of your billing period.

No refunds for partial billing periods.