Need help getting item (struct) by id from list and return that item. (beginner)

I want to create a list containing structs. Then get specific item from list and return in a function. What am I doing wrong. Im very new to this language but I have tried to look at the documentation but I would love a helping hand. Anyone know how to do this?

struct Foo {
    id: u8,
    name: String
}

fn get_foo_by_id(id: u8) -> Foo {

    let mut foo_1 = Attraction {
        id: 2,
        name: String::from("Foo 1"),
    };

    let mut foo_2 = Attraction {
        id: 2,
        name: String::from("Foo 2"),
    };

    let mut foo_3 = Attraction {
        id: 3,
        name: String::from("Foo 3"),
    };

    let mut listOfFoos: Vec<Foo> = Vec::new();
    listOfFoos.push(foo_1);
    listOfFoos.push(foo_2);
    listOfFoos.push(foo_3);

    let mut iter = listOfFoos.into_iter().find(|a| a.id == id);
    println!("{}",iter.name); //Error: no field `name` on type `std::option::Option<Attraction>`
    iter //Error: ^^^^ expected struct `Foo`, found enum `std::option::Option`
}

What do you want to do if you pass in an id that doesn't exist in your set of attractions?

If you don't care, you can panic by using Option::unwrap, but this is discouraged because it makes your program harder to use.

To better handle this case, you can use

fn get_foo_by_id(id: u8) -> Option<Foo> {
    let mut listOfFoos: Vec<Foo> = Vec::new();
    
    // .. init listOfFoos ...
    
    let attraction = listOfFoos.into_iter().find(|a| a.id == id)?;
                                               // note the `?`  ^
    
    println!("{}",attraction.name);
    
    Some(attraction)
}

The question mark operator tries to take out the value inside an Option and if it can't it will make the enclosing function return None.

i.e.

foo?

is the same as

match foo {
    Some(x) => x,
    None => return None
}
3 Likes

If you are looking for a reasonable performance you may consider using HashMap which is optimised specifically for your use case.

use std::collections::HashMap;

get_foo_by_id(id: u8) -> Option<Foo> {
  //..Considering foo_* already defined before..
  let mut foos = HashMap::new();
  foos.insert(foo_1.id, foo_1);
  foos.insert(foo_2.id, foo_2);
  foos.insert(foo_3.id, foo_3);
  foos.get(id).take() // notice take() - we convert Option<&Foo> into Option that owns Foo
}

Of course get_foo_by_id is just a sample, in real app foos HashMap would be initialized and filled outside of this function context within done structure which will hold your data:

struct Foos {
  data: HashMap<u8, Foo>
}

impl Foos {
  pub fn new() -> Self {
    Foos { data: HasMap::new() }
  }
  pub fn insert(&mut self, foo: Foo) -> Option<Foo> {
    self.data.insert(foo.id, foo)
  }
  pub fn get_by_id(&self, id: u8) -> Option<&Foo> {
    self.data.get(id)
  }
}
1 Like