【问题标题】:Returning a RWLockReadGuard independently from a method独立于方法返回 RWLockReadGuard
【发布时间】:2018-05-23 20:23:16
【问题描述】:

我有一个类型的对象

Arc<RwLock<SessionData>>

我有一个方法应该引用SessionData

fn some_method(session: ...)

我正在使用 Rocket(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,这将超出范围。

相反,我正在尝试创建某种自给自足的类型,该类型将包含 Arc&lt;RwLock&lt;SessionData&gt;&gt;,包含此锁的锁守卫,并取消引用到 SessionData 对象。

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

  • 一个包含Arc&lt;RwLock&lt;SessionData&gt;&gt; 和一个RwLockReadGuard&lt;SessionData&gt;Session 对象
  • 一个对象,其中包含来自owning-ref 库的Arc&lt;RwLock&lt;SessionData&gt;&gt;RwLockReadGuardRef&lt;SessionData&gt;
  • 将使用来自owning-ref 库的OwnedHandle 类型的对象。

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

是否有可能创建一种自包含的类似“句柄”的对象,该对象将包含锁定和锁定它指向的对象的锁定保护? p>

这与How to return reference to a sub-value of a value that is under a mutex? 中描述的情况相似,但略有不同。在那里,MutexGuardRef 内部依赖于Mutex,如果Mutex(或MyStruct)超出范围,则无法存在。为了实现类似的行为,我必须传递一个包含我的RwLock 的结构,然后在方法内部进行锁定。这很好,但我想知道我是否可以更进一步,并传递一个既 独立 又用作 RwLockGuard 的结构,从而避免需要手动锁定。

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

【问题讨论】:

    标签: rust borrow-checker rwlock


    【解决方案1】:

    Why can't I store a value and a reference to that value in the same struct? 中所述,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());
    }
    

    另见:

    【讨论】:

    • 哇,这太棒了。如果可以的话,我会把你淹没在我所有的赞成票中。这正是我需要的。非常感谢!
    • 现在出租的回购是只读的,有没有更新的推荐?
    【解决方案2】:

    如果有人需要其他解决方案,那么这里是 @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());
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-08-11
      • 2013-05-12
      • 1970-01-01
      • 2011-01-19
      • 2021-03-15
      • 1970-01-01
      相关资源
      最近更新 更多