error[E0521]: borrowed data escapes outside of method
--> src\main.rs:44:9
|
43 | async fn do_something(&mut self) {
| ---------
| |
| `self` is a reference that is only valid in the method body
| let's call the lifetime of this reference `'1`
44 | / some_func(|| {
45 | | self.x = 100;
46 | | });
| | ^
| | |
| |__________`self` escapes the method body here
| argument requires that `'1` must outlive `'static`
Now I get that because the closure captures the reference of self and combined with 'static on some_func, the borrow checker can't be sure that self will be valid when the closure is eventually executed. Is that correct?
Now the million dollar question. Whats the right way to do something like this?
For completeness, the package I'm using is called callback-future which allows me to turn a callback into a future. The full code I want to get working is:
Use other "referencing" mechanisms other than &-borrowing.
In Rust it is customary to:
use Arc (or downgraded to a Weak) to get a new : 'static "referencing handle" to the Arc-wrapped value,
this, in turn, by design, involves multiple handles being able to access the resource, i.e., involves sharing the resource / concurrent access, i.e., you will only have & access to it. Which is why you'll then need &-compatible mutability wrappers, called shared mutability wrappers (or interior mutability even though I personally don't like that name), to compensate.
Hence the pervasive Arc<Mutex<T>>, or Arc<RwLock<T>>. In fact, ::callback-future uses that itself!
or, often better, to use channels. They're less flexible but thus able to be a more optimized implementation of Arc<Mutex<OptionOrVec<T>>>
In your case, using the latter:
use some::channel; // _e.g._, `std::sync::mpsc` to begin with
type CompletionCb<T> = Box<dyn 'static + Send + FnOnce(T)>;
// in your struct:
cb: Option<channel::Receiver<CompletionCb<String>>>,
async fn some_async(&mut self) -> Result<String, i32> {
let (sender, receiver) = channel::new();
self.cb = Some(receiver);
// trick: when requiring `'static`, put this
// vvvv
CallbackFuture::new(move |completion_cb| {
sender.send(completion_cb).ok();
})
.await
}
and then you can do cb.{try_,}recv() to get access to the completion cb:
if let Ok(resolve) = self.cb.as_ref().unwrap().try_recv() {
resolve("Hello, World!".into()); // assert_eq!(self.some_async().await, "Hello, World!")
}
XY problem
Note that if you are gonna be using channels, then you may as well skip the CallbackFuture wrapper altogether, which is redundantly reïmplementing yet another async-channel-like API, potentially less efficiently than how the proper such channels do it: