Reputation: 712
I have Arc<Mutex<_>> data in an enum. Now I would like to call a function on the enum and pass a closure/callback so that the enum will unlock the inner data and pass it through the closure before closing the lock.
use std::ops::Deref;
use std::sync::{Arc, Mutex};
#[derive(Default)]
struct Note {
nothing: (),
// ...
}
/// a data struct that contains references to every field of the base struct
struct NoteData<'a> {
nothing: &'a (),
// ...
}
/// a function to instantiate a data struct
fn note_to_data(note: &Note) -> NoteData {
NoteData {
nothing: ¬e.nothing,
}
}
/// a model that encapsulates multiple possible entities
enum Model {
Note(Arc<Mutex<Note>>),
// ...
}
impl Model {
/// a function that allows performing a calculation on the data struct without
/// needing to export a mutable reference outside of the enum, which would cause locking issues
pub fn with_data<'a>(&'a self, func: impl Fn(NoteData<'a>)) {
match self {
Model::Note(note) => {
let lock : MutexGuard<Note> = note.lock().unwrap();
let borrowed : &Note = lock.deref();
let data : NoteData = note_to_data(borrowed);
func(data); // the data struct with the references that depend on the lock are moved and dropped. Why is the lock complaining it still doesn't live long enough?
// ... "`lock` dropped here while still borrowed"
}
}
}
}
#[test]
fn test_closure() {
let model = Model::Note(Arc::new(Mutex::new(Note::default())));
model.with_data(|data| {
println!("{:?}", data.nothing);
});
}
It doesn't work however and I'm puzzled why that is the case, since the NoteData struct is dropped by moving it to the 'func' closure call and should not outlive the lock. (other answer where exactly this works)
The error is the following:
error[E0597]: `lock` does not live long enough
--> lib/experiment/src/lib.rs:28:32
|
24 | pub fn with_data<'a>(&'a self, func: impl Fn(NoteData<'a>)) {
| -- lifetime `'a` defined here
...
28 | let borrowed = lock.deref();
| ^^^^^^^^^^^^
| |
| borrowed value does not live long enough
| argument requires that `lock` is borrowed for `'a`
...
31 | }
| - `lock` dropped here while still borrowed
Upvotes: 0
Views: 132
Reputation: 27303
The problem is that you connected the two lifetimes, the one on &'a self
and the one passed into the closure of NoteData<'a>
by reusing the same lifetime name.
You can easily solve the problem by just not doing that, again don't blindly plug in the same lifetime everywhere just because you have one name unless of course you know what you're doing and that's actually what you want.
In fact any lifetimeparameter dictated from outside the function is no good, for example this doesn't work either:
pub fn with_data<'a, 'b>(&'a self, func: impl Fn(NoteData<'b>)) {
because like 'a
'b
has to live for the whole body, even after the function returns, but that's too long since the lock
goes out of scope before that.
What you can do however is use a higher ranked trait bound to specify that the closure has to be able to deal with any lifetime you throw at it:
pub fn with_data<'a>(&'a self, func: impl for<'b> Fn(NoteData<'b>)) {
but since that is quite a mouth full, the programmers of Rust have invented something called the elision rules, and they allow us to just not mention any of the lifetimes, but have them essentialy be that last example.
pub fn with_data(&self, func: impl Fn(NoteData)) {
Upvotes: 1
Reputation: 712
After messing around it suddenly compiled when doing this:
impl Model {
pub fn with_data<R>(&self, func: impl Fn(NoteData) -> R) -> R {
match self {
Model::Note(note) => func(note_to_data(note.lock().unwrap().deref())),
}
}
}
Upvotes: 0