Kaligule
Kaligule

Reputation: 754

update and output a datastructure repeatedly

I have a mutable data structure (fruitlist: Vec<&mut Groceries>) that can't be easily copied. I want to repeatedly update and output it. Here is a minimal example:

// Note that Groceries do not implement the Copy trait                                                                   
struct Groceries {
    amount: u32,
    item: String,
}

// takes mutable references because we will alter the Groceries                                                          
fn update_groceries(groceries: Vec<&mut Groceries>) {
    // Double everything we have, just in case of a pandemic                                                             
    for g in groceries {
        g.amount *= 2;
    }
}

// takes immutable references because we will only need to read them                                                     
fn output_groceries(groceries: Vec<&Groceries>) {
    for g in groceries {
        println!("{}: {}", g.item, g.amount);
    }
}

fn main() {
    // We start with a list of fruit                                                                                     
    let mut bananas: Groceries = Groceries{amount:3, item: String::from("yellow bananas")};
    let mut oranges: Groceries = Groceries{amount:2, item: String::from("oranges")};
    let fruitlist: Vec<&mut Groceries> = vec![
        &mut bananas,
        &mut oranges,
    ];

    // Now we want to update and print the list                                                                          

    update_groceries(fruitlist);
    // the following line is necessary to get from Vec<&mut Groceries> to Vec<& Groceries>                               
    let fruitlist_immutable_references: Vec<& Groceries> = fruitlist.into_iter().map(|x| &*x).collect();
    output_groceries(fruitlist_immutable_references);

    // This update and print part might happen multiple times in a loop.                                                 
}

This fails because the fruitlist is getting moved:

error[E0382]: use of moved value: `fruitlist`
  --> src/main.rs:38:60
   |
29 |     let fruitlist: Vec<&mut Groceries> = vec![
   |         --------- move occurs because `fruitlist` has type `std::vec::Vec<&mut Groceries>`, which does not implement the `Copy` trait
...
36 |     update_groceries(fruitlist);
   |                      --------- value moved here
37 |     // the following line is necessary to get from Vec<&mut Groceries> to Vec<& Groceries>
38 |     let fruitlist_immutable_references: Vec<& Groceries> = fruitlist.into_iter().map(|x| &*x).collect();
   |                                                            ^^^^^^^^^ value used here after move

The code works fine when I comment out the updating.

I find myself running into this problem in different forms again and again. How can I make this pattern work? Or is there a better way to update and output a complex data structure repeatedly?

(Also the line defining fruitlist_immutable_references is pretty ugly. I hope that there is a better way)

Upvotes: 0

Views: 54

Answers (1)

pretzelhammer
pretzelhammer

Reputation: 15135

If you pass the list by value (e.g. list: Vec<&mut Groceries>) into a function then it moves the ownership of the list into the function. Instead you should pass it by reference (e.g. list: &mut [&mut Groceries], or list: &[&Groceries]) to maintain ownership of the list. Fixed example:

// Note that Groceries do not implement the Copy trait                                                                   
struct Groceries {
    amount: u32,
    item: String,
}

// takes mutable references because we will alter the Groceries                                                          
fn update_groceries(groceries: &mut [&mut Groceries]) {
    // Double everything we have, just in case of a pandemic                                                             
    for g in groceries {
        g.amount *= 2;
    }
}

// takes immutable references because we will only need to read them                                                     
fn output_groceries(groceries: &[&Groceries]) {
    for g in groceries {
        println!("{}: {}", g.item, g.amount);
    }
}

fn main() {
    // We start with a list of fruit                                                                                     
    let mut bananas: Groceries = Groceries { amount:3, item: String::from("yellow bananas") };
    let mut oranges: Groceries = Groceries { amount:2, item: String::from("oranges") };
    let mut fruitlist: Vec<&mut Groceries> = vec![
        &mut bananas,
        &mut oranges,
    ];

    // Now we want to update and print the list                                                                          
    update_groceries(&mut fruitlist);
    
    // the following line is necessary to get from Vec<&mut Groceries> to Vec<& Groceries>                               
    let fruitlist_immutable_references: Vec<&Groceries> = fruitlist.into_iter().map(|x| &*x).collect();
    output_groceries(&fruitlist_immutable_references);

    // This update and print part might happen multiple times in a loop.                                                 
}

playground

Upvotes: 4

Related Questions