从方法中独立返回RWLockReadGuard

6

我有一个类型为

Arc<RwLock<SessionData>>

我有一个方法,应该接收某种对SessionData的引用。

fn some_method(session: ...)

我正在使用Rust的Web框架Rocket,由于该框架调用方法,我不能直接调用。不过,我可以提供一个实现来创建一个对象,并将其传递给处理程序。代码类似于:

impl<'a, 'r> request::FromRequest<'a, 'r> for SomeType {
    type Error = ();

    fn from_request(request: &'a request::Request<'r>) -> request::Outcome<Self, Self::Error> {
        // return object here
    }
}

我希望避免直接返回一个RwLock,因为我希望处理程序传递已经锁定的对象。但是,我不能返回引用或RwLockReadGuard,因为它们都依赖于RwLock,而RwLock会超出范围。
相反,我正在尝试创建一种自给自足的类型,该类型将包含Arc<RwLock<SessionData>>,包含对此锁的锁定保护以及解除对SessionData对象的引用。
到目前为止,我尝试了以下组合:
- 包含Arc<RwLock<SessionData>>RwLockReadGuard<SessionData>Session对象 - 包含Arc<RwLock<SessionData>>和来自owning-ref库的RwLockReadGuardRef<SessionData>的对象。 - 将使用owning-ref库中的OwnedHandle类型的对象。
然而,我无法实现想要做的事情,遇到了各种生命周期借用问题等问题。
是否有可能创建一种类似于Handle的自包含对象,该对象包含指向其所指向的对象的锁和锁定保护?
这是与How to return reference to a sub-value of a value that is under a mutex?描述的情况类似但略有不同。在那里,MutexGuardRef内部依赖于Mutex,如果Mutex(或MyStruct)超出范围,则无法存在。为了实现类似的行为,我必须传递一个包含我的RwLock的结构,然后在方法内进行锁定。这很好,但我想知道是否可以再进一步,传递既是独立的又作为RwLockGuard的结构,避免手动锁定的需要。
基本上,我想将RwLock的锁定从客户端移动到值的提供者。
2个回答

9

正如在为什么无法在同一个结构体中存储值和对该值的引用?中所述,Rental crate允许在某些情况下使用自引用结构体。

#[macro_use]
extern crate rental;

use std::sync::{Arc, RwLock};

struct SessionData;
impl SessionData {
    fn hello(&self) -> u8 { 42 }
}

rental! {
    mod owning_lock {
        use std::sync::{Arc, RwLock, RwLockReadGuard};

        #[rental(deref_suffix)]
        pub struct OwningReadGuard<T>
        where
            T: 'static,
        {
            lock: Arc<RwLock<T>>,
            guard: RwLockReadGuard<'lock, T>,
        }
    }
}

use owning_lock::OwningReadGuard;

fn owning_lock(session: Arc<RwLock<SessionData>>) -> OwningReadGuard<SessionData> {
    OwningReadGuard::new(session, |s| s.read().unwrap())
}

fn main() {
    let session = Arc::new(RwLock::new(SessionData));

    let lock = owning_lock(session.clone());
    println!("{}", lock.hello());

    assert!(session.try_read().is_ok());
    assert!(session.try_write().is_err());

    drop(lock);

    assert!(session.try_write().is_ok());
}

参见:


哇,这太棒了。如果可以的话,我会用所有的赞淹没你。这正是我所需要的。非常感谢! - Samuel Moriarty
现在Rental的 repo 是只读的,有没有更新的建议? - Joseph Garvin

3

如果有人需要另一种解决方案,那么这里是使用ouroboros重写的@Shelpmaster的示例:

use ouroboros::self_referencing;
use std::sync::{Arc, RwLock, RwLockReadGuard};

struct SessionData;
impl SessionData {
    fn hello(&self) -> u8 {
        42
    }
}

#[self_referencing]
pub struct OwningReadGuard<T>
where
    T: 'static,
{
    lock: Arc<RwLock<T>>,
    #[covariant]
    #[borrows(lock)]
    guard: RwLockReadGuard<'this, T>,
}

fn owning_lock(lock: Arc<RwLock<SessionData>>) -> OwningReadGuard<SessionData> {
    OwningReadGuardBuilder {
        lock,
        guard_builder: |l: &Arc<RwLock<SessionData>>| l.read().unwrap(),
    }
    .build()
}

fn main() {
    let session = Arc::new(RwLock::new(SessionData));

    let lock = owning_lock(session.clone());
    println!("{}", lock.borrow_guard().hello());

    assert!(session.try_read().is_ok());
    assert!(session.try_write().is_err());

    drop(lock);

    assert!(session.try_write().is_ok());
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接