从互斥量中借用数据“借来的价值不够长”

时间:2015-08-18 21:41:25

标签: rust

如何在互斥体内的数据上返回迭代器,互斥体本身包含在结构中。编译器给出的错误是“借来的值不够长”。

如何将值的生命周期延伸到外部范围?

这是我想要实现的最小演示。

use std::sync::{Mutex, Arc};
use std::vec::{Vec};
use std::slice::{Iter};

#[derive(Debug)]
struct SharedVec {
  pub data: Arc<Mutex<Vec<u32>>>,
}

impl SharedVec {
  fn iter(& self) -> Iter<u32> {
    self.data.lock().unwrap().iter()
  }
}

fn main() {

  let sv = SharedVec {
    data: Arc::new(Mutex::new(vec![1, 2, 3, 4, 5]))
  };

  for element in sv.data.lock().unwrap().iter() {  // This works
    println!("{:?}", element);
  }

  for element in sv.iter() {  // This does not work
    println!("{:?}", element);
  }
}

Rust操场链接:http://is.gd/voukyN

2 个答案:

答案 0 :(得分:7)

你不能完全按照你在这里写的方式去做。

Rust中的互斥锁使用RAII模式进行获取和释放,也就是说,当您在其上调用相应的方法并获取特殊保护值时,您将获得互斥锁。当该防护范围超出范围时,互斥锁将被释放。

为了使这种模式安全,Rust使用其借用系统。您只能通过lock()返回的防护来访问互斥锁内的值,而您只能通过引用 - MutexGuard<T>实现Deref<Target=T>DerefMut<Target=T>来执行此操作,这样您就可以从中获取&T&mut T

这意味着您从互斥值中获得的每个值都必须将其生命周期与守卫的生命周期相关联。但是,在您的情况下,您尝试返回Iter<u32>,其生命周期参数与self的生命周期相关联。以下是iter()方法的完整签名,没有生命周期参数省略,其主体带有显式临时变量:

fn iter<'a>(&'a self) -> Iter<'a, u32> {
    let guard = self.data.lock().unwrap();
    guard.iter()
}

此处guard.iter()结果的生命周期与一个guard相关联,'a严格小于guard,因为iter()仅存在于方法主体的范围内。这违反了借用规则,因此编译器失败并出现错误。

guard返回时,ScrollView被销毁并且锁被释放,因此Rust实际上阻止了您发出实际的逻辑错误! C ++中的相同代码将编译和行为不正确,因为您将访问受保护的数据而不锁定它,至少导致数据争用。再一次证明了Rust的力量:)

我认为如果没有标准类型的讨厌的hacks或样板包装,你将无法做你想做的事情。我个人认为这很好 - 您必须尽可能明确地管理您的互斥锁,以避免死锁和其他令人讨厌的并发问题。 Rust已经让你的生活变得更加轻松,因为它通过借用系统强制缺少数据竞争,这正是守卫系统如上所述行为的原因。

答案 1 :(得分:2)

正如 Vladimir Matveev's answer 所提到的,这对于返回值是不可能的。如果将迭代器传递给函数而不是返回它,则可以实现您的目标:

impl SharedVec {
    fn iter<R>(&self, func: impl FnOnce(Iter<'_, u32>) -> R) -> R {
        let guard = self.data.lock().unwrap();
        func(guard.iter())
    }
}

这个函数是这样使用的:

sv.iter(|iter| {
    for element in iter {
        println!("{:?}", element);
    }
});

这种类型的函数包装必须对每种类型的迭代器重复。如果你最终这样做,可能更容易交出一个可变切片或 &mut SharedVec 代替,让闭包选择迭代方法。

此方法有效,因为您永远不会释放锁,以防止多个线程同时写入数据。