generally it's nice to avoid unnecessary allocation for things like this. one first simple improvement would be to return a Vec<&str> instead of a String so you don't have to clone the string data:
pub fn get_image_ids(&self) -> Vec<&str> {
self.images.iter().map(|v| v.id.as_str()).collect()
}
next, you can skip the collect() call in get_image_ids and instead return an iterator:
pub fn get_image_ids(&self) -> impl Iterator<Item = &str> {
self.images.iter().map(|v| v.id.as_str())
}
that way the caller can decide whether they want to allocate a vec or not. if they choose to just iterate over the values without collecting then no extra allocation is required with this setup