Search code examples
rustreference

Getting non ref value from iter method


let shoes = vec![
    Shoe {
        size: 10,
        style: String::from("sneaker"),
    },
    Shoe {
        size: 13,
        style: String::from("sandal"),
    },
    Shoe {
        size: 10,
        style: String::from("boot"),
    },
];

let shoe_size = 10;
let filtered_shoes: Vec<&Shoe> = shoes.iter().filter(|s| s.size == shoe_size).collect();
let shoe_0: &Shoe = shoes.get(0).unwrap();
println!("address vec shoe {:p}", shoes);
println!("address filtered shoe {:p}", filtered_shoes);
println!("{:?}", filtered_shoes);
println!("{:?}", shoe_0);

Given collect fn following the filter operation, why is the return type Vec<&Shoe> instead of Vec<Shoe>. Please note that into_iter is not a possibility here.

In other languages like javascript, filter would copy from the original and would return new vector. Printing the address of those 2 vectors shows two different addresses.

The other was part how to make shoe_0 have the type of Shoe instead of &Shoe?


Solution

  • That's one of the selling points of Rust, it doesn't hide creating copies or otherwise managing the memory form you like JavaScript, a garbage collected language would. Instead, you have to explicitly instruct it to do so. For that you need a way to convert references to a shoe (&Shoe) into owned Shoes, the most common being deriving Clone for Shoe and cloning the shoes:

    #[derive(Clone)]
    struct Shoe { /* … */ }
    

    then you can use the iterator adapter .cloned() to convert an Iterator<Item = &Shoe> into an Iterator<Item = Shoe>

    let filtered_shoes: Vec<Shoe> = shoes.iter().cloned().filter(|s| s.size == shoe_size).collect();
    

    and similarly you can .clone() a &Shoe to turn it into a Shoe:

    let shoe_0: Shoe = shoes.get(0).unwrap().clone();