【问题标题】:Deref a Arc<RwLock<T>> to return RwLockReadGuard<T> by default默认情况下,取消引用 Arc<RwLock<T>> 以返回 RwLockReadGuard<T>
【发布时间】:2021-06-10 07:59:48
【问题描述】:

我有以下 Arc&lt;RwLock&lt;T&gt;&gt; 的包装器,我想取消它们以默认返回 RwLockReadGuard&lt;T&gt;

use anyhow::{Result, bail};
use serde::{Deserialize, Serialize};
use std::ops::Deref;
use std::sync::{Arc, Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard};

#[derive(Serialize, Deserialize, Debug, Clone, Default)]
pub struct ArcRwLock<T: Sized>(Arc<RwLock<T>>);

impl<T> ArcRwLock<T> {
    pub fn new(data: T) -> Self {
        ArcRwLock(Arc::new(RwLock::new(data)))
    }

    pub fn write(&self) -> Result<RwLockWriteGuard<T>> {
        match self.0.try_write() {
            Ok(x) => Ok(x),
            Err(e) => {
                bail!(
                    "RwLock cannot acquire writer lock, error: {}",
                    e.to_string()
                )
            }
        }
    }

    pub fn read(&self) -> RwLockReadGuard<T> {
        self.0.read().unwrap()
    }

}

// impl<T: Sized> Deref for ArcRwLock<T> {
//     type Target = RwLockReadGuard<T>;

//     #[inline]
//     fn deref(&self) -> &Self::Target {
//         self.0.read().unwrap()
//     }
// }

impl<T: PartialEq> PartialEq for ArcRwLock<T> {
    fn eq(&self, other: &Self) -> bool {
        if Arc::ptr_eq(&self.0, &other.0) && ::core::ptr::eq(&*self.0, &*other.0) {
            true
        } else {
            *other.0.read().unwrap().deref() == *self.0.read().unwrap().deref()
        }
    }
}

我为PartialEq编写了上面的包装器,我需要一个父结构来正确#[derive(PartialEq)]

大多数时候我从Arc&lt;RwLock&lt;T&gt;&gt; 读取值T,很少写入。

上述实现允许我使用以下方式读取/写入值:

some_arc_object.write()?.uuid = Uuid::new_v4();
let updated_uuid: T = some_arc_object.read().uuid;
// where uuid is a field of T

由于我大部分时间都在阅读属性,我想摆脱重复的.read(),并通过取消引用整个 Arc 来实现以下目标:

let updated_uuid: T = some_arc_object.uuid;
// instead of having to add .read()
// let updated_uuid: T = some_arc_object.read().uuid;

我目前的谦虚尝试显示在上面的评论部分,试图让deref() 以与.read() 相同的方式工作。但是编译器对返回局部变量的引用并不满意。有没有可能通过一些终生的魔法或其他解决方法来实现它?

【问题讨论】:

    标签: rust lifetime ownership


    【解决方案1】:

    按设计取消引用应该只解析指向指针的智能指针,参见例如Rust API Guidelines。在Deref 中获取像MutexGuardRwLockReadGuard 这样的同步保护肯定超出了此准则,可能会导致细微的错误。 IE。你可以在不显式调用lock()read()write() 的情况下获得资源的隐式锁定,因为它隐藏在Deref impl 中,在解析方法调用时会隐式调用。

    至于原因为什么这是不可能的:Deref 的返回类型是一个引用,因此您需要返回一些可以从deref() 转换为引用的东西。 RwLockReadGuard 是您在 deref() 范围内创建的值,因此它在范围的末尾被删除,这反过来意味着您不能分发对它的引用。

    有时,将您需要对 RwLock 中的值执行的任何操作封装在一个函数中可能是符合人体工程学的,即,如果它是一个 String,有时会被写入但大多数时候您只是想阅读它,定义一些方便的方法,如下所示:

    struct Foo {
        shared: Arc<RwLock<String>>,
    }
    impl Foo {
        fn get_shared_str(&self) -> String {
            self.shared.read().unwrap().clone()
        }
    }
    

    【讨论】:

    • 感谢您的回答!我不了解 API 指南,但在阅读之后,我确实意识到将引用转换为 Guard 的问题。但是我可以把整个事情都转入 &T 吗?它还允许我直接使用some_arc_object.uuid 并且似乎没有违反指南。?
    • 无法通过RwLock 解除对&amp;T 的引用,因为这正是RwLockMutex 正在阻止的事情!只有在同步原语允许时才能访问。因此,只有 RwLockReadGuardMutexGuard 可以 deref 到 &amp;T 并且您需要通过显式访问方法获取它们
    猜你喜欢
    • 2020-11-28
    • 1970-01-01
    • 1970-01-01
    • 2021-10-18
    • 1970-01-01
    • 2020-01-16
    • 2023-03-12
    • 1970-01-01
    • 2017-10-08
    相关资源
    最近更新 更多