Есть ли способ запустить замыкание в RwLock drop? - PullRequest
0 голосов
/ 08 ноября 2019

У меня есть программа, которая скрывает изменяемое состояние за 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 ]

2 голосов
/ 08 ноября 2019

Вы можете сделать это с типом оболочки:

Детская площадка

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, чтобы автоматизировать это.

Это становится довольно многословным, поэтому я нашел ящик, который подразумевает для вас черту разыскивания .

Я до сих пор не уверен, что означают замыкания, упомянутые вами в названии.

0 голосов
/ 10 ноября 2019

Вы можете создать оболочку, которая реализует 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);
        }
    }
}

Для удобства вы можете захотеть также реализовать Deref и DerefMut, чтобы вы могли напрямую вызывать методы для нее:

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();
    },
));
...