Problem overview
The function oldest_pet receives a vector pets. Each pet is a tuple (String, i32): its name and its age. It returns the name of the oldest pet. When several pets share the oldest age, the one that comes first in the vector wins.
For [("Rex", 4), ("Bella", 7), ("Max", 2)] the answer is "Bella". For [("Luna", 3), ("Coco", 9), ("Milo", 9)] it is "Coco", because Coco and Milo are both 9 and Coco is listed first. A single pet, [("Kiki", 0)], gives "Kiki".
Approach
Go through the pets once and remember the oldest one seen so far. Before the loop sees anything, the first pet is the best candidate. Each later pet replaces it only if it is strictly older. The word strictly handles ties: a pet with the same age as the current best does not replace it, so the earlier pet stays.
Step by step
- Start with a reference to the first pet.
let mut oldest = &pets[0];borrows the pet instead of moving it out of the vector, andmutletsoldestpoint at a different pet later. - Loop over
&petswith the loop variablepet, another reference to a tuple. - Compare ages. A pet's age is its
.1, so the test ispet.1 > oldest.1, with a strict greater-than. - Move the marker. Inside the
if,oldest = pet;. Both sides are references, so nothing is copied;oldestjust points at this pet. - Return the name.
oldest.0.clone()makes a newStringfor the return value. The name in the vector stays where it is, becauseoldestonly borrows it.
The solution in Rust
impl Solution {
fn oldest_pet(pets: Vec<(String, i32)>) -> String {
let mut oldest = &pets[0];
for pet in &pets {
if pet.1 > oldest.1 {
oldest = pet;
}
}
return oldest.0.clone();
}
}
Two Rust details are worth noticing. The reference oldest may not outlive pets, and it doesn't, because both live until the function ends. And the function must clone() the name: it can't return a reference to data that is dropped when pets goes out of scope.
Following the loop
For [("Luna", 3), ("Coco", 9), ("Milo", 9)], oldest points at these pets as the loop moves on:
| pet | oldest after the check |
|---|---|
| Luna | Luna |
| Coco | Coco |
| Milo | Coco |
Milo is 9, the same as Coco, so the strict test fails and Coco stays.
Another way, and a trap
An iterator method looks like a shortcut: pets.iter().max_by_key(|p| p.1) finds the pet with the largest age. But when several items share the largest key, max_by_key returns the last of them. On the second example it gives "Milo", not "Coco", so it breaks the tie rule of this lesson. The explicit loop with a strict comparison keeps the first.
Testing
On the three examples the function returns:
Bella
Coco
Kiki
Edge cases and common mistakes
- Using
>=. It would let the later pet win a tie, so the second example would return"Milo". - Starting with age 0 and no pet. An age can be 0, as with Kiki, so a made-up starting age would need a name too. Starting from the first real pet avoids that.
- Returning
oldest.0withoutclone(). That tries to move aStringout of a borrowed tuple, which the compiler rejects.
