【问题标题】:Cannot return reference to temporary value with RwLock and iterators无法使用 RwLock 和迭代器返回对临时值的引用
【发布时间】:2022-10-25 04:14:51
【问题描述】:

我在其他问题中没有找到答案。

我已将我的问题减少到以下几点:

use std::sync::RwLock;

pub fn main() {
    iter_lock().for_each(|v| {
        println!("{}", v);
    });
}

fn get_lock<'a>() -> &'a RwLock<Vec<u32>> {
    static mut lock: RwLock<Vec<u32>> = RwLock::new(Vec::new());
    unsafe { &lock }
}

fn iter_lock<'a>() -> impl std::iter::Iterator<Item = &'a u32> {
    get_lock().read().unwrap().iter()
}

playground

上面的代码将无法编译并给出以下错误:

error[E0515]: cannot return reference to temporary value
  --> src/main.rs:15:5
   |
15 |     get_lock().read().unwrap().iter()
   |     --------------------------^^^^^^^
   |     |
   |     returns a reference to data owned by the current function
   |     temporary value created here
   |
   = help: use `.collect()` to allocate the iterator
  • 请注意,上面的代码中不需要静态 mut,但我需要它,因为我需要在 impl 块内定义一个变量。
  • 我需要返回一个迭代器,而不是 Vec,因为我试图避免任何分配,并且该函数将始终用于迭代。

我该如何解决这个问题?我不怕使用不安全的代码,所以也欢迎不安全的建议。

【问题讨论】:

  • 除了@SvenMarnach 所说的(他至少曾经在上面发表过评论!),想想你想要什么:我相信你希望你的迭代器保留read() 调用的结果,并保留RwLockReadGuard只要迭代器还活着,就活着。如果是这样,我认为这将是一个要返回的新结构,它将RwLockReadGuard 移动到自身中,并充当您想要的迭代器。这不是.iter() 返回的内容。
  • 我对这里使用可变静态感到困惑。您的函数get_lock() 实际上是正确的,但有点奇怪。首先,使用生命周期参数'a 毫无意义。您应该返回一个带有 'static 生命周期的引用。其次,使用可变静态是没有意义的,因为你永远不会改变它。删除 mut 限定符后,您就不再需要 unsafe 了。 (Playground)
  • @KevinAnderson 啊,没想到。这很有意义
  • @SvenMarnach你是对的,这个例子中不需要静态 mut 。但在原始版本中,我确实对 RwLock 后面的数据进行了变异。不确定是使用'a 还是'static,所以谢谢你的提示!
  • @Jomy 即使您在锁后面改变数据,锁本身仍然不需要是mut。这就是重点。

标签: rust reference iterator


【解决方案1】:

你可以尝试这样的事情:

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

pub fn main() {
    let data = Data::new(&[1, 2, 3]);

    data.iter().for_each(|x| println!("{:?}", x));
}


struct Data {
    inner: RwLock<Vec<u32>>,
}

impl Data {
    fn new(vec: &[u32]) -> Self {
        Self {
            inner: RwLock::new(vec.to_vec()),
        }
    }

    fn iter(&self) -> Iter<'_> {
        let d = self.inner.read().unwrap();
        Iter::new(d)
    }
}

struct Iter<'a> {
    inner: RwLockReadGuard<'a, Vec<u32>>,
    current_index: usize,
}

impl<'a> Iter<'a> {
    pub fn new(inner: RwLockReadGuard<'a, Vec<u32>>) -> Iter<'a> {
        Self {
            inner,
            current_index: 0,
        }
    }
}

impl Iterator for Iter<'_> {
    type Item = u32;

    fn next(&mut self) -> Option<Self::Item> {
        if self.current_index >= self.inner.len() {
            return None;
        }

        let item = &self.inner[self.current_index];
        self.current_index += 1;
        Some(*item)
    }
}

【讨论】:

  • 在结构中添加锁确实可以解决它
  • 不是要让你失望,但是Data 比原始的RwLock 本身更好吗?您的iterator 方法只是一步调用read()unwrap() 而不是两步,并带有更多样板。您在main 中的行实际上是相同的,除了iterator() 替换为.read().unwrap()。还是我错过了什么?
  • @KevinAnderson 更新示例
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-09-02
  • 1970-01-01
  • 2011-07-23
  • 2017-03-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多