Question
How can I write a Rust function that accepts an iterator, performs an operation on its items, and returns a result? For example, I want to pass the values iterator from a HashMap and return its smallest value:
use std::collections::HashMap;
fn find_min<'a>(vals: Iterator<Item = &'a u32>) -> Option<&'a u32> {
vals.min()
}
fn main() {
let mut map = HashMap::new();
map.insert("zero", 0u32);
map.insert("one", 1u32);
println!("Min value: {:?}", find_min(map.values()));
}
This produces errors about trait objects, Sized, and mismatched iterator types. Passing the iterator by reference produces similar errors, while using Box leads to lifetime errors. What is the correct way to define this function?
Short Answer
Rust iterator methods work with many concrete iterator types, including HashMap::values(). You will learn why Iterator is a trait rather than a concrete parameter type, and how to accept iterators with a generic type parameter or impl Iterator.
Concept
Iterator is a trait: it describes behavior rather than naming one specific concrete type. Many types implement it, such as ranges, slices, vectors, and HashMap value views.
map.values() does not produce a value whose concrete type is literally Iterator<Item = &u32>. It produces a concrete library type similar to hash_map::Values<'_, &str, u32>, and that type implements the Iterator trait.
A function parameter must normally have a known size at compile time. Writing this is therefore not valid:
// Invalid: Iterator is a trait, not a concrete sized type.
fn find_min<'a>(vals: Iterator<Item = &'a u32>) -> Option<&'a u32> {
vals.min()
}
Use one of these forms instead:
fn find_min<'a>(vals: impl Iterator<Item = &'a u32>) -> Option<&'a u32>
or the equivalent explicit generic form:
fn find_min<'a, I>(vals: I) -> Option<&'a u32>
where
I: Iterator<Item = &'a u32>,
Both say: “accept any concrete type I that implements Iterator and yields &'a u32 values.”
This matters because iterator adapters such as .map(), .filter(), and .enumerate() create their own concrete iterator types. Generic iterator parameters let one function work with all of them without allocating or boxing.
Mental Model
Think of Iterator as a job description, not a physical object.
Iteratorsays: “I can provide items one at a time.”HashMap::Valuesis one particular worker that has that job.- A range such as
1..10is another worker with the same job.
A parameter written as I: Iterator means “send me any worker qualified for this job.” Rust still knows the worker's exact concrete type when compiling, so it knows its size and can optimize the code.
A trait object such as dyn Iterator is different: it is like hiding the worker's exact identity behind a common interface. That can be useful, but it requires indirection such as &mut dyn Iterator<_> or Box<dyn Iterator<_>>. For ordinary iterator-consuming functions, generics are usually simpler.
Syntax and Examples
Use impl Iterator when the function consumes an iterator and only needs iterator behavior:
fn find_min<'a>(vals: impl Iterator<Item = &'a u32>) -> Option<&'a u32> {
vals.min()
}
A complete example with HashMap::values():
use std::collections::HashMap;
fn find_min<'a>(vals: impl Iterator<Item = &'a u32>) -> Option<&'a u32> {
vals.min()
}
fn main() {
let mut map = HashMap::new();
map.insert("zero", 0u32);
map.insert("one", 1u32);
map.insert("two", 2u32);
println!("Min value: {:?}", find_min(map.values()));
}
Output:
Min value: Some(0)
map.values() yields &u32 because the map owns its u32 values. The function returns Option<&u32> for the same reason:
Some(&0)when at least one value exists.Nonewhen the map is empty.
The explicit generic version is equivalent and is useful when you want to name the iterator type:
fn find_min<'a, I>(vals: I) -> Option<&'a u32>
where
I: Iterator<Item = &'a u32>,
{
vals.min()
}
Step by Step Execution
Consider this example:
use std::collections::HashMap;
fn find_min<'a>(vals: impl Iterator<Item = &'a u32>) -> Option<&'a u32> {
vals.min()
}
fn main() {
let mut map = HashMap::new();
map.insert("apple", 12u32);
map.insert("pear", 4u32);
map.insert("orange", 9u32);
let smallest = find_min(map.values());
println!("{smallest:?}");
}
Execution:
mapstores three key-value pairs.map.values()creates aValuesiterator. It borrows the map and yields references like&12,&4, and&9.- Rust chooses the concrete
Valuestype for theimpl Iteratorparameter at this call site. .min()consumes the iterator, comparing each yielded&u32by the numeric value of the referencedu32.- The smallest reference,
&4, is returned asSome(&4). - The reference is valid only while
mapremains alive and unchanged in ways that conflict with the borrow.
The iteration order of a HashMap is not sorted and should not be relied on. .min() examines all values, so it still finds the correct minimum regardless of iteration order.
Real World Use Cases
Iterator-accepting functions are useful whenever your logic should work with multiple data sources.
- Report calculations: Find the smallest invoice amount from a vector, database result iterator, or map values.
- Validation: Check whether any submitted field is empty with
.any(...). - Data cleanup: Filter invalid records and transform valid ones with
.filter(...)and.map(...). - API processing: Consume pages or streamed records one item at a time.
- Log analysis: Find the latest timestamp or highest severity level without first creating another collection.
For example, this function works with any iterator of numbers:
fn count_large(values: impl Iterator<Item = u32>) -> usize {
values.filter(|value| *value > 100).count()
}
let from_vec = count_large(vec![50, 120, 250].into_iter());
let from_range = count_large(90..105);
assert_eq!(from_vec, 2);
assert_eq!(from_range, 4);
Real Codebase Usage
In real Rust code, choose the parameter based on what your function needs.
Consume an existing iterator
Use impl Iterator or a generic I: Iterator when callers already have an iterator:
fn has_negative(values: impl Iterator<Item = i32>) -> bool {
values.any(|value| value < 0)
}
This consumes values because methods such as .any() take ownership of the iterator.
Accept collections and iterators
Use IntoIterator when callers may pass a collection directly or an iterator:
fn sum_nonzero<I>(values: I) -> i32
where
I: IntoIterator<Item = i32>,
{
values.into_iter().filter(|value| *value != 0).sum()
}
assert_eq!(sum_nonzero(vec![1, 0, 3]), 4);
assert_eq!(sum_nonzero(1..4), 6);
Use iterator chains instead of temporary collections
A common pattern is to pass an adapter chain directly:
fn first_long_name<'a>(names: impl Iterator<Item = &'a str>) -> Option<&'a str> {
names.find(|name| name.len() >= 8)
}
let names = ["Ada", "Grace", "Margaret"];
let result = first_long_name(names.iter().copied());
assert_eq!(result, Some("Margaret"));
This avoids allocating an intermediate Vec solely to perform another operation.
Common Mistakes
Using a trait as though it were a concrete type
// Does not compile.
fn find_min<'a>(values: Iterator<Item = &'a u32>) -> Option<&'a u32> {
values.min()
}
Iterator has no known size by itself. Use impl Iterator or a generic type bound.
fn find_min<'a>(values: impl Iterator<Item = &'a u32>) -> Option<&'a u32> {
values.min()
}
Expecting an iterator to be reusable after consumption
let values = vec![3, 1, 2].into_iter();
let smallest = values.min();
// values.count(); // Error: values was moved into min().
Most terminal iterator methods, including .min(), consume the iterator. Create another iterator if you need another pass.
Returning a reference after the owner is gone
// Does not compile: the map is dropped before the returned reference could be used.
fn invalid() -> Option<&'static u32> {
let map = std::collections::HashMap::from([("one", 1u32)]);
find_min(map.values())
}
A returned &u32 points into the map, so the map must outlive the reference. Return an owned u32 instead when the result must outlive the collection:
fn find_min_owned(values: impl Iterator<Item = u32>) -> Option<u32> {
values.min()
}
Adding Box unnecessarily
// Usually more complex than necessary.
fn find_min<'a>(values: Box<dyn Iterator<Item = &'a u32> + 'a>) -> Option<&'a u32> {
values.min()
}
This can work in some designs, but it allocates on the heap and adds lifetime details. Prefer generics unless you specifically need to store or return different iterator types through one runtime interface.
Assuming HashMap iteration is ordered
HashMap does not promise insertion order or sorted key order. .min() is safe because it checks every item, but do not treat the first value yielded by map.values() as the smallest or earliest value.
Comparisons
| Approach | Best when | Example parameter | Notes |
|---|---|---|---|
impl Iterator | The function accepts and consumes one iterator | values: impl Iterator<Item = u32> | Concise syntax for a generic parameter. |
Generic I: Iterator | You need to name or reuse the iterator type in bounds | fn f<I: Iterator>(values: I) | Equivalent in capability to one impl Iterator parameter. |
IntoIterator | Callers might pass a collection or an iterator | values: impl IntoIterator<Item = u32> | Call .into_iter() inside the function. |
&mut impl Iterator | The caller should retain the iterator and its current position | values: &mut impl Iterator<Item = u32> | The function advances the caller's iterator. |
&mut dyn Iterator | Runtime polymorphism is genuinely needed | values: &mut dyn Iterator<Item = u32> | No heap allocation, but dynamic dispatch. |
Box<dyn Iterator> | You must own a dynamically selected iterator | Box<dyn Iterator<Item = u32>> | Heap allocation and dynamic dispatch. |
For the original find_min function, impl Iterator is the clearest choice. If you want callers to pass a Vec<u32> directly, use IntoIterator<Item = u32> and return an owned number.
Cheat Sheet
// Accept any iterator that yields owned u32 values.
fn total(values: impl Iterator<Item = u32>) -> u32 {
values.sum()
}
// Equivalent generic form.
fn total_generic<I>(values: I) -> u32
where
I: Iterator<Item = u32>,
{
values.sum()
}
// Accept a collection or iterator.
fn total_any(values: impl IntoIterator<Item = u32>) -> u32 {
values.into_iter().sum()
}
// Accept references and return a reference.
fn find_min<'a>(values: impl Iterator<Item = &'a u32>) -> Option<&'a u32> {
values.min()
}
Iteratoris a trait, not a concrete parameter type.impl Iterator<Item = T>accepts one compiler-known concrete iterator type per call.- Terminal methods such as
.min(),.sum(),.collect(),.count(), and.any()consume the iterator. Item = &Tmeans the iterator yields borrowed values; returned references cannot outlive their owner.- Use
IntoIteratorwhen accepting collections directly is more ergonomic. HashMap::values()yields references to the map's values.
FAQ
Why does Iterator<Item = T> fail as a function parameter in Rust?
Iterator is a trait, and traits do not have a known concrete size by default. Use impl Iterator<Item = T>, a generic type parameter such as I: Iterator<Item = T>, or a trait object behind &mut or Box.
Should I use impl Iterator or I: Iterator?
Use impl Iterator for simple function parameters. Use I: Iterator when you need to refer to the iterator type by name, such as in multiple bounds or associated type relationships.
Why does find_min(map.values()) return Option<&u32>?
map.values() borrows values stored inside the map, so its items are &u32. The minimum is therefore also a reference, wrapped in Option for the empty-map case.
Can I return Option<u32> instead?
Yes, if you iterate over owned u32 values or copy the references:
let min: Option<u32> = map.values().copied().min();
Does calling .min() sort a HashMap?
No. .min() scans the iterator and remembers the smallest item. It does not modify or sort the map.
When should I use Box<dyn Iterator>?
Use it when a value must hold one of several different iterator types selected at runtime, such as returning different iterator pipelines from different branches. It is usually unnecessary for a function that only processes an iterator immediately.
Why does passing an iterator by reference change the signature?
A reference changes the parameter type and borrowing behavior. Use &mut impl Iterator<Item = T> only when the function should advance an iterator that the caller continues to own. For a one-time operation like .min(), passing the iterator by value is usually right.
Mini Project
Description
Build a small utility for analyzing product prices stored in a HashMap. The utility should accept any iterator of borrowed prices, find the lowest price, and keep the returned reference tied safely to the original map.
Goal
Print the name-independent lowest product price from a HashMap by passing map.values() to a reusable iterator function.
Requirements
Use a HashMap containing at least three product names and u32 prices.
Write a lowest_price function that accepts any iterator yielding &u32.
Return Option<&u32> so an empty input is handled safely.
Call the function with prices.values().
Print the result with Debug formatting.
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.
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.
Accessing Cargo Package Metadata in Rust
Learn how to read Cargo package metadata like version, name, and authors in Rust using compile-time environment macros.