【问题标题】:Return a reference to a T inside a lazy static RwLock<Option<T>>?在惰性静态 RwLock<Option<T>> 中返回对 T 的引用?
【发布时间】:2020-11-28 19:10:30
【问题描述】:

我有一个惰性静态结构,我希望能够在程序执行开始时将其设置为某个随机值,然后再获取。可以拿这个小傻sn-p做例子:

use lazy_static::lazy_static;
use std::sync::RwLock;

struct Answer(i8);

lazy_static! {
    static ref ANSWER: RwLock<Option<Answer>> = RwLock::new(None);
}

fn answer_question() {
    *ANSWER.write().unwrap() = Some(Answer(42));
}

fn what_is_the_answer() -> &'static Answer {
    ANSWER
        .read()
        .unwrap()
        .as_ref()
        .unwrap()
}

这段代码编译失败:

error[E0515]: cannot return value referencing temporary value
  --> src/lib.rs:15:5
   |
15 |        ANSWER
   |   _____^
   |  |_____|
   | ||
16 | ||         .read()
17 | ||         .unwrap()
   | ||_________________- temporary value created here
18 | |          .as_ref()
19 | |          .unwrap()
   | |__________________^ returns a value referencing data owned by the current function

我知道您不能返回对临时值的引用。但我想返回对ANSWER 的引用,它是静态的——与临时相反!我猜是第一次调用unwrap 返回的RwLockReadGuard 是问题所在?

我可以通过改变返回类型来编译代码:

fn what_is_the_answer() -> RwLockReadGuard<'static, Option<Answer>> {
    ANSWER
        .read()
        .unwrap()
}

但是现在调用代码变得非常不符合人体工程学 - 我必须进行两次额外调用才能获得实际值:

what_is_the_answer().as_ref().unwrap()

我可以从这个函数中返回对静态ANSWER 的引用吗?我可以通过某种方式映射返回RwLockReadGuard&lt;&amp;Answer&gt; 吗?

【问题讨论】:

  • 正如您所猜想的那样,该示例将不起作用,因为返回的引用(来自选项)绑定到LockGuard,该LockGuard 在函数末尾被删除。借用检查器阻止对ANSWER 的引用从锁中逃逸。你到底想在这里完成什么?
  • @user2722968 我想在程序启动时设置一个全局变量(基于配置文件中的某些设置),并且我希望以后能够从程序中的各个位置以符合人体工程学的方式访问它。一切都发生在同一个线程中。
  • @user2722968 我进行了编辑以添加更多信息。

标签: rust readwritelock lazy-static


【解决方案1】:

once_cell 就是为此而设计的:在answer_question 中使用.set(...).unwrap(),在what_is_the_answer 中使用.get().unwrap()

【讨论】:

    【解决方案2】:

    据我了解您的意图,Answer 的值在 lazy_static 中初始化时无法计算,但取决于仅在调用 answer_question 时才知道的参数。以下可能不是最优雅的解决方案,但它允许将 &amp;'static-reference 引用到依赖于仅在运行时知道的参数的值。

    基本方法是使用两个lazy_static-值,其中一个用作“代理”来进行必要的同步,另一个是值本身。这样可以避免每次访问 ANSWER 时都必须访问多层锁和解包 Option-values。

    ANSWER-value 通过等待 CondVar 进行初始化,这将在计算值时发出信号。然后将该值放在lazy_static 中,从那时起就不可移动了。因此&amp;'static 是可能的(参见get_the_answer())。我选择了String 作为示例类型。请注意,访问ANSWER 而不调用generate_the_answer() 将导致初始化永远等待,从而使程序死锁。

    use std::{sync, thread};
    
    lazy_static::lazy_static! {
        // A proxy to synchronize when the value is generated
        static ref ANSWER_PROXY: (sync::Mutex<Option<String>>, sync::Condvar) = {
            (sync::Mutex::new(None), sync::Condvar::new())
        };
        // The actual value, which is initialized from the proxy and stays in place
        // forever, hence allowing &'static access
        static ref ANSWER: String = {
            let (lock, cvar) = &*ANSWER_PROXY;
            let mut answer = lock.lock().unwrap();
            loop {
                // As long as the proxy is None, the answer has not been generated
                match answer.take() {
                    None => answer = cvar.wait(answer).unwrap(),
                    Some(answer) => return answer,
                }
            }
        };
    }
    
    // Generate the answer and place it in the proxy. The `param` is just here
    // to demonstrate we can move owned values into the proxy
    fn generate_the_answer(param: String) {
        // We don't need a thread here, yet we can
        thread::spawn(move || {
            println!("Generating the answer...");
            let mut s = String::from("Hello, ");
            s.push_str(&param);
            thread::sleep(std::time::Duration::from_secs(1));
    
            let (lock, cvar) = &*ANSWER_PROXY;
            *lock.lock().unwrap() = Some(s);
            cvar.notify_one();
            println!("Answer generated.");
        });
    }
    
    // Nothing to see here, except that we have a &'static reference to the answer
    fn get_the_answer() -> &'static str {
        println!("Asking for the answer...");
        &ANSWER
    }
    
    fn main() {
        println!("Hello, world!");
    
        // Accessing `ANSWER` without generating it will deadlock!
        //get_the_answer();
    
        generate_the_answer(String::from("John!"));
        println!("The answer is \"{}\"", get_the_answer());
    
        // The second time a value is generated, noone is listening.
        // This is the flipside of `ANSWER` being a &'static
        generate_the_answer(String::from("Peter!"));
        println!("The answer is still \"{}\"", get_the_answer());
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-11-08
      • 2020-08-12
      • 2012-08-28
      • 2012-10-23
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多