Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
828 views
in Technique[技术] by (71.8m points)

rust - Borrow data out of a mutex "borrowed value does not live long enough"

How can I return an iterator over data within a mutex which itself is contained within a struct. The error the compiler gives is "borrowed value does not live long enough".

How do I get the lifetime of the value to extend into the outer scope?

Here is a minimal demo of what I am trying to achieve.

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 playground link: http://is.gd/voukyN

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

You cannot do it exactly how you have written here.

Mutexes in Rust use RAII pattern for acquisition and freeing, that is, you acquire a mutex when you call the corresponding method on it which returns a special guard value. When this guard goes out of scope, the mutex is released.

To make this pattern safe Rust uses its borrowing system. You can access the value inside the mutex only through the guard returned by lock(), and you only can do so by reference - MutexGuard<T> implements Deref<Target=T> and DerefMut<Target=T>, so you can get &T or &mut T out of it.

This means that every value you derive from a mutexed value will necessarily have its lifetime linked to the lifetime of the guard. However, in your case you're trying to return Iter<u32> with its lifetime parameter tied to the lifetime of self. The following is the full signature of iter() method, without lifetime parameters elision, and its body with explicit temporary variables:

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

Here the lifetime of guard.iter() result is tied to the one guard, which is strictly smaller than 'a because guard only lives inside the scope of the method body. This is a violation of borrowing rules, and so the compiler fails with an error.

When iter() returns, guard is destroyed and the lock is released, so Rust in fact prevented you from making an actual logical error! The same code in C++ would compile and behave incorrectly because you would access protected data without locking it, causing data races at the very least. Just another demonstration of the power of Rust :)

I don't think you'll be able to do what you want without nasty hacks or boilerplate wrappers around standard types. And I personally think this is good - you have to manage your mutexes as explicit as possible in order to avoid deadlocks and other nasty concurrency problems. And Rust already makes your life much easier because it enforces absence of data races through its borrowing system, which is exactly the reason why the guard system behaves as described above.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...