Skip to content

2. Find a user by ID

Find the user with a given ID in a list of users.

  1. WorseAn index loop over &Vec<User>, a full copy of the user, and a panic when the ID is not in the list.

    pub fn find_user(users: &Vec<User>, id: u64) -> User {
    for i in 0..users.len() {
    if users[i].id == id {
    return users[i].clone();
    }
    }
    panic!("user {id} not found");
    }
  2. OKAn iterator and Option: no panic. But .cloned() copies the user, with its strings, on every call.

    pub fn find_user(users: &[User], id: u64) -> Option<User> {
    users.iter().find(|user| user.id == id).cloned()
    }
  3. BetterOption<&User>: no panic and no copy. The caller decides if it needs its own copy.

    pub fn find_user(users: &[User], id: u64) -> Option<&User> {
    users.iter().find(|user| user.id == id)
    }

The “Worse” version is a translation of a Java method with a for loop that throws NoSuchElementException. Every line of it is a small problem:

With Option<&User>, the caller handles the missing case in the type, and borrows the user that it found:

pub fn greeting(users: &[User], id: u64) -> String {
match find_user(users, id) {
Some(user) => format!("Hello, {}", user.name),
None => "Hello, guest".to_string(),
}
}
Each version compared with the Worse version
CriterionOKBetter
Correctness Better Better
A missing user is None, not a panic.
Failure behavior Better Better
The caller must handle None. The compiler checks it.
Performance Same Better
"OK" still copies the user. "Better" copies nothing.
Readability Better Better
One line instead of seven.
API ergonomics Better Better
A slice parameter accepts a Vec, an array, or part of a list.
  • “OK” is correct when the caller needs to own the user, for example to store it after the list is changed. Even then, it is often better to return Option<&User> and let that one caller write .cloned().
  • A linear search is fine for a short list. For many lookups in a long list, a HashMap<u64, User> is the better design, and map.get(&id) already returns Option<&User>.
  • A panic can be correct if the ID comes from your own code and a missing user is a bug. Then use expect with a message that says why the user must exist, and keep the function name honest, for example user_by_known_id.