use std::sync::Mutex;
pub struct First {
amutex:Mutex<Second>,
}
pub struct Second {
avec:Vec<u8>,
}
impl First {
fn getRange(&self,start:usize,stop:usize) -> &[u8] {
let refvec=&self.amutex.lock().unwrap();
&(*refvec).avec.as_slice()[start..stop]
}
}
fn main() {
let f=First {
amutex:Mutex::new(Second {
avec:vec![0,2,4,6,8,10],
}),
};
let range=f.getRange(1,3);
for dataitem in range {
println!("{}",dataitem);
}
}
Compiling playground v0.0.1 (/playground)
error[E0515]: cannot return value referencing temporary value
--> src/main.rs:14:9
|
13 | let refvec=&self.amutex.lock().unwrap();
| --------------------------- temporary value created here
14 | &(*refvec).avec.as_slice()[start..stop]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ returns a value referencing data owned by the current function
For more information about this error, try `rustc --explain E0515`.
error: could not compile `playground` due to previous error
I was under the impression that I have created a reference. I assume the mutexguard is the temporary created but that does not need to survive for this code to work, according to my understanding.
Indeed, you want shared, and in this instance, immutable, access to the Second('s Vec) instance.
But if the lock guard is released, then Rust cannot guarantee lack of mutation between the moment where it is released and the moment where you are looking at the start .. stop range of that vec, hence the error.
There are, in general, three kinds of solutions for your problem, here:
you can expose the Mutex (or at least a locking function) and have getRange be a method on Second (or the guard yielded by the locking function):
impl Second { fn getRange(…) { … } }
let locked_f = f.some_lock_function();
let range = locked_f.getRange(1, 3);
for dataitem in range { … }
You can offer a callback / scoped API, which allows you to have your own code run after the caller's, which means you can give access to the inner borrow to the caller:
impl First {
fn with_range<R> (
&self,
start: usize,
stop: usize,
// "return" value
// vvvvv
with: impl FnOnce(&[u8]) -> R,
) -> R
{
let guard = self.amutex.lock().unwrap();
with(&guard.avec[start .. stop])
}
}
let f = First { … }
f.with_range(1, 3, |range| {
for dataitem in range {
…
}
}); // <- lock released here
This is an underrated pattern, imho.
And finally, you can, for a simple case such as this one, offer "sugar" for the previous callback-based API by offering your own lock guard:
In general, the very point of "guard" objects is that they are the token with which you will be able to do useful things to the guarded object. It wouldn't make sense for a Mutex to hand out guards if they didn't actually enforce the invariant of the mutex (i.e. that it is held while someone is poking around in the locked object).