Replace the content of an RwLockWriteGuard

Viewed 1207

Let's assume the following code:

use std::sync::RwLock;

pub struct NotCloneable(u8);

pub struct Foo {
    value: RwLock<Vec<NotCloneable>>,
}

impl Foo {
    // does not work
    pub fn filter_out_values(&self) {
        let mut guard = self.value.write().unwrap();
        *guard = guard.into_iter().filter(|nc| nc.0 != 0).collect();
    }
}
error[E0507]: cannot move out of borrowed content
  --> src/lib.rs:12:18
   |
12 |         *guard = guard.into_iter().filter(|nc| nc.0 != 0).collect();
   |                  ^^^^^ cannot move out of borrowed content

(playground)

How do I make the function filter_out_values work?

2 Answers

The special circumstance here is, that your T is not Cloneable, therefore you cannot use guard.iter().filter(...).cloned().collect().

I see two options here.

  1. Instead of RwLock<Vec<NotCloneable>> you could use RwLock<Option<Vec<NotCloneable>>> and then use Option::take() to get the value the RwLock was holding and leaving None

  2. You could use std::mem::take(), which is a shortcut for std::mem::replace(v, Default::default()), to get the vec from the guard without triggering the error, because there is no way that you leave the value of the RwLock in an undefined state, where it does not hold any value

use std::sync::RwLock;

pub struct NotCloneable(u8);

pub struct Foo {
    value: RwLock<Vec<NotCloneable>>,
}

impl Foo {
    pub fn filter_out_values(&self) {
        let mut guard = self.value.write().unwrap();
        let vec = std::mem::take(&mut *guard);
        *guard = vec.into_iter().filter(|nc| nc.0 != 0).collect();
    }
}

pub struct Foo1 {
    value: RwLock<Option<Vec<NotCloneable>>>,
}

impl Foo1 {
    pub fn filter_out_values(&self) {
        let mut guard = self.value.write().unwrap();
        let vec = guard.take();
        *guard = Some(vec.unwrap().into_iter().filter(|nc| nc.0 != 0).collect());
    }
}

(playground)

While the other answer is correct, in this case, I'd recommend you to use Vec::retain():

impl Foo {
    pub fn filter_out_values(&self) {
        let mut guard = self.value.write().unwrap();
        guard.retain(|nc| nc.0 != 0);
    }
}

It will also be faster.

Related