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\"
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.