有没有办法在RwLock drop上运行闭包?

时间:2019-11-08 00:33:42

标签: rust raii

我有一个程序,将可变状态隐藏在RwLock后面。我想做的是,当将它可变地借用(RW_LOCK.write())时,它应该执行某些操作(即,尝试写入文件,清理rwlock后面的数据等)

例如:

let DATA: RwLock<Data> = RwLock::new(Data { content: Default::default() } );

fn do_something() {
    let mut state = DATA.write().unwrap();

    state.change(5);
    // ...
    // Here, just before `state` goes out of scope (where it gets dropped and `RwLock` will allow 
    // other threads read/write access to `Data`, I would like for `RwLock` to auto-run `state.cleanup()`. 
}

是否可以执行此操作,还是必须重新实现RwLock

2 个答案:

答案 0 :(得分:2)

您可以使用包装类型:

Playground

use std::ops::{Deref, DerefMut, Drop};
use std::sync::{RwLock, RwLockWriteGuard};

type CleanupClosure<'a> = Fn(&mut RwLockWriteGuard<'a, Data>);

struct Data {
    content: String,
}

impl Data {
    fn change(&mut self, num: i32) {
        println!("Changed to {}", num);
        self.content = num.to_string();
    }
}

struct RwLockWriteWrapper<'a, F: CleanupClosure<'a>>(RwLockWriteGuard<'a, Data>, F);

impl<'a, F: CleanupClosure<'a>> Deref for RwLockWriteWrapper<'a, F> {
    type Target = RwLockWriteGuard<'a, Data>;

    fn deref(&self) -> &RwLockWriteGuard<'a, Data> {
        &self.0
    }
}

impl<'a, F: CleanupClosure<'a>> DerefMut for RwLockWriteWrapper<'a, F> {
    fn deref_mut(&mut self) -> &mut RwLockWriteGuard<'a, Data> {
        &mut self.0
    }
}

impl<'a, F: CleanupClosure<'a>> Drop for RwLockWriteWrapper<'a, F> {
    fn drop(&mut self) {
        println!("Cleaning up!");
        self.1(&mut self.0)
    }
}

fn main() {
    let data: RwLock<Data> = RwLock::new(Data {
        content: "Start".to_owned(),
    });
    do_something(&data);
    do_something(&data);
}

fn do_something(data: &RwLock<Data>) {
    // Write your own cleanup logic here
    let mut state = RwLockWriteWrapper(data.write().unwrap(), |state| {
        state.content = "Cleaned up".to_owned()
    });

    println!("do_something start: {}", state.content);
    state.change(5);

    println!("do_something after change: {}", state.content);
} // Automatically run cleanup here

它确实要求您在调用.write()时记住包装该类型。您可以将RwLock本身包装成另一种类型,该类型也将返回RwLockWriteWrapper以使其自动化。

这确实变得很冗长,所以我发现了a crate that impls the deref trait for you

我仍然不确定标题中提到的闭包是什么。

答案 1 :(得分:0)

您可以创建实现Drop的包装器:

struct CleanOnDrop<D, F>
where
    F: FnOnce(&mut D),
{
    data: D,
    cleanup: Option<F>,
}

impl<D, F> CleanOnDrop<D, F>
where
    F: FnOnce(&mut D),
{
    pub fn new(data: D, cleanup: F) -> Self {
        Self { data, cleanup: Some(cleanup) }
    }
}

impl<D, F> Drop for CleanOnDrop<D, F>
where
    F: FnOnce(&mut D)
{
    fn drop(&mut self) {
        if let Some(mut cleanup) = self.cleanup.take() {
            cleanup(&mut self.data);
        }
    }
}

为方便起见,您可能还希望实现DerefDerefMut,以便可以直接在其上调用方法:

use std::ops::{Deref, DerefMut};

impl<D, F> Deref for CleanOnDrop<D, F>
where
    F: FnOnce(&mut D),
{
    type Target = D;
    fn deref(&self) -> &D {
        &self.data
    }
}

impl<D, F> DerefMut for CleanOnDrop<D, F>
where
    F: FnOnce(&mut D),
{
    fn deref_mut(&mut self) -> &mut D {
        &mut self.data
    }
}

像这样使用包装器:

let data = RwLock::new(CleanOnDrop::new(
    Data {
        content: Default::default(),
    },
    |state| {
        state.cleanup();
    },
));