Problem overview
The function price_list receives a vector of strings, items, and a vector of integers, prices, of the same length. The price at each index belongs to the item at the same index. It returns a vector with one line per item, like "1. apple: 3": the item's number, then the item, a colon and the price. Numbers start at 1 and the lines keep the order of items.
For ["apple", "bread", "milk"] and [3, 2, 4] the answer is ["1. apple: 3", "2. bread: 2", "3. milk: 4"]. A single item, ["tea"] with [5], gives ["1. tea: 5"]. Repeated names stay separate lines, and a price may be 0.
Approach
Each line needs three things at once: the position, the item and its price. Walking over two vectors side by side is what zip does, and enumerate adds the position. Chaining them gives items of the form (index, (item, price)), and the loop can unpack that shape directly. The index starts at 0, so the printed number is i + 1.
Step by step
- Create
linesas an empty vector withVec::new(), declaredlet mutbecause the loop will add to it. - Loop over
items.iter().zip(prices.iter()).enumerate()with the pattern(i, (item, price)). The outer parentheses unpack the pair thatenumeratemakes, and the inner ones unpack the pair thatzipmade. - Push a line built with
format!("{}. {}: {}", i + 1, item, price).format!works likeprintln!but returns aStringinstead of printing. - Return
linesafter the loop.
The solution in Rust
impl Solution {
fn price_list(items: Vec<String>, prices: Vec<i32>) -> Vec<String> {
let mut lines = Vec::new();
for (i, (item, price)) in items.iter().zip(prices.iter()).enumerate() {
lines.push(format!("{}. {}: {}", i + 1, item, price));
}
return lines;
}
}
Here item and price are references, because iter() borrows. format! prints through a reference exactly as it would print the value, so no * is needed. The order of the adapters matters: zip(...).enumerate() gives (i, (item, price)), while enumerate().zip(...) would give ((i, item), price) and the pattern would not match.
The index version
The same lines can be built by looping over the positions and indexing both vectors:
for i in 0..items.len() {
lines.push(format!("{}. {}: {}", i + 1, items[i], prices[i]));
}
With items ["tea", "jam"] and prices [5, 0] it gives ["1. tea: 5", "2. jam: 0"]. It works, but it relies on both vectors having the same length: a shorter prices would panic with an index out of bounds. Iterators avoid that kind of slip, and Rust code usually prefers them.
Why Vec::new works without a type
The line let mut lines = Vec::new(); does not say what the vector holds. Rust works it out from the rest of the function: push adds a String made by format!, and the function returns Vec<String>. If nothing in the function fixed the type, the compiler would ask for an annotation.
Following the loop
For the first example, the loop sees these values:
i=0 item=apple price=3
i=1 item=bread price=2
i=2 item=milk price=4
Each pass adds one to i for the printed number, so the lines come out as 1, 2 and 3 in the same order as items.
Testing
Two of the examples give:
["1. apple: 3", "2. bread: 2", "3. milk: 4"]
["1. pen: 2", "2. pen: 3", "3. ink: 0"]
Edge cases and common mistakes
- Numbering from 0. Printing
iinstead ofi + 1would start the list at 0. - A wrong pattern.
(i, item, price)does not match the nested shape and is a type error. - Different lengths.
zipstops at the end of the shorter vector without an error: items["x", "y"]zipped with three prices give two pairs. The constraints here promise equal lengths, so nothing is lost.
