5

我有一个类型的对象

Arc<RwLock<SessionData>>

我有一种方法应该参考SessionData

fn some_method(session: ...)

我正在使用 Rocket(Rust 的网络框架),我不能直接调用该方法,因为它是由 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直接返回一个,因为我希望处理程序将一个已经锁定的对象传递给它。但是,我不能返回引用或 a RwLockReadGuard,因为它们都依赖于RwLock,这将超出范围。

相反,我正在尝试创建某种自给自足的类型,该类型将包含一个Arc<RwLock<SessionData>>,包含对该锁的锁定保护,并取消对一个SessionData对象的引用。

到目前为止,我已经尝试了以下一些组合:

  • Session包含 anArc<RwLock<SessionData>>和 a的对象RwLockReadGuard<SessionData>
  • 包含来自owning-ref库的 anArc<RwLock<SessionData>>和 a的对象。RwLockReadGuardRef<SessionData>
  • 将使用OwnedHandle来自owning-ref库的类型的对象。

然而,我一直没能做我想做的事,遇到各种终生借贷问题等等。

是否有可能创建一种自包含的类似“句柄”的对象,该对象将同时包含它指向的对象的锁和锁守卫?

这与如何返回对位于互斥锁下的值的子值的引用中描述的情况类似,但略有不同?. 在那里,MutexGuardRef内部依赖于,如果(or ) 超出范围Mutex,则不存在。为了实现类似的行为,我必须传递一个包含 my 的结构,然后在方法内部进行锁定。这很好,但我想知道我是否可以更进一步,并传递一个既独立又用作a 的结构,从而避免手动锁定的需要。MutexMyStructRwLockRwLockGuard

基本上,我想将锁定RwLock从客户端移动到值的提供者。

4

2 回答 2

8

为什么我不能在同一个结构中存储一个值和对该值的引用中所述?,在某些情况下, 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());
}

也可以看看:

于 2018-05-23T21:54:15.653 回答
1

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

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());
}
于 2021-08-18T07:36:51.813 回答