Search code examples
rustmutex

How to properly mutate a vector behind a Mutex in Rust


I have a stuct that looks like this:

pub struct Coordinator {
    map_tasks: Mutex<Vec<Task>>,
    reduce_tasks: Mutex<Vec<Task>>,
}

And I have a function that is called with a parameter to determine which of the two vectors to modify. The function takes a type and an id to find in the vector and modify. My original approach is this:

impl Coordinator {
    pub fn update_status(&self, task_type: TaskType, task_id: u8) {
        if task_type == TaskType::Map {
            let tasks = &mut self.map_tasks.lock().unwrap();
            for task in tasks.iter_mut() {
                if task.task_id == task_id {
                    task.status = TaskStatus::Success;
                }
            }
        } else {
            let tasks = &mut self.reduce_tasks.lock().unwrap();
            for task in tasks.iter_mut() {
                if task.task_id == task_id {
                    task.status = TaskStatus::Success;
                }
            }
        }
    }
}

I would like to shorten the code since a lot of it is duplicated. I've tried this, but it doesnt work with the error temporary value dropped while borrowed, consider using a "let" binding to create a longer lived value

impl Coordinator {
    pub fn update_status(&self, task_type: TaskType, task_id: u8) {
        let tasks;
        if task_type == TaskType::Map {
            tasks = &mut self.map_tasks.lock().unwrap();
        } else {
            tasks = &mut self.reduce_tasks.lock().unwrap();
        }

        for task in tasks.iter_mut() {
            if task.task_id == task_id {
                task.status = TaskStatus::Success;
            }
        }
    }
}

How could I fix this?


Solution

  • You don't need to take a mutable reference into the guard, you can just store the guard directly. Also, if..else is an expression, you can use it as a ternary:

    impl Coordinator {
        pub fn update_status(&self, task_type: TaskType, task_id: u8) {
            let mut tasks = if task_type == TaskType::Map {
                self.map_tasks.lock().unwrap()
            } else {
                self.reduce_tasks.lock().unwrap()
            };
    
            for task in tasks.iter_mut() {
                if task.task_id == task_id {
                    task.status = TaskStatus::Success;
                }
            }
        }
    }