I have a worker and it accepts some functions to execute,just like
struct App {
worker: Worker
}
impl Worker {
pub fn run<F>(&self, f: F) where F: (FnOnce() -> Box<dyn Updater>) + Send + 'static,{
todo!()
}
}
the worker is a property of app, the closure received by the worker maybe will capture references in the environment, the references come from the app structure. What should I do
It's not clear what problem you are facing. Perhaps provide a concrete example code illustrating that? Anyway, the most straight-forward way to annotate lifetime is like this:
impl Worker {
pub fn run<'a, F>(&self, f: F)
where F: (FnOnce() -> Box<dyn Updater + 'a>) + Send + 'a
{
todo!()
}
}
if you are calling the woker to process some states of the App
, you probably don't want to store Worker
as a field of App
, otherwise, you just flight the borrow check for no good. when it comes to struct composition, you should really think about what owns what. when I was first learning rust coming from a C++ background, I tried to store every states in a big struct, typically called Context
, Manager
or the like, just for convinience. but the compiler doesn't allow me, so I put Rc
or Cell
everywhere. it took me quite some time to be able to think and design data structure "the rust way".
in your paticular case, you might workaround by store both App
and Worker
as fields of some larger struct.
if you have a self: &mut App
, you might workaround it by store the worker
field as a Option<Worker>
, and you take the worker out before calling worker.run(|| {...})
, and place it back after the call. you can do achieve similar effect with Cell<Worker>
, but you should be careful about what to put inside the cell when you take the worker out.
if you have to store the worker as a field of app, you can try partial-borrow
2 Likes
struct App {
openid: String,
helper: OmsHelper,
worker: Worker
...
}
impl eframe::App for App {
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
...
let mut button1 = DebounceButton::from_memory("***",1, ctx);
if button1.show(ui).clicked() {
self.worker.run(||{
let rs = self.helper.create_prepare_order();
Box::new(rs)
});
button1.store_memory(ctx);
}
}
}
struct Worker {
thread: thread::JoinHandle<()>,
jobs: Arc<Mutex<Vec<Job>>>,
}
impl Worker {
pub fn run<F>(&self, f: F) where F: (FnOnce() -> Box<dyn Updater>) + Send + 'static,{
loop {
let tl = self.jobs.try_lock();
match tl {
Ok(mut v) => {
let job = Box::new(f);
v.push(job);
break;
},
Err(e) => {}
}
}
}
}
This is the roughly complete code
It's not a runnable code snippet, so I assume you are trying creating some Job
for background worker thread to do when a button is clicked. And the problem arose from the fact that the created Job
is borrowing App
.
Simply speaking, just avoid borrow App
if possible. For example: Does create_prepare_order
requires &mut OmsHelper
? if not, you can wrap the helper in Arc
and clone the Arc
to avoid borrowing App
. If it does, then you have to wrap some kind of synchronize mechanic.
Btw, it seems you are trying to emulate a thread-safe queue with Arc<Mutex<Vec<Job>>>
. You can use std::sync::mpsc::channel
instead. (And loop { self.jobs.try_lock() ... }
is just a worse version of let jobs = self.jobs.lock()
)
Thanks for your help.
I think about "wrap some kind of synchronize mechanic" this topic, maybe i just can use Arc<Mutex> to resolve this problem. If you have better solution, tell me plz.
std::sync::mpsc::channel is a good solution 
Arc<Mutex<T>>
is the "easy" way, or the quickest way to get your code running. Since I have no idea how OmsHelper::create_prepare_order
work I can't provide much more information.
One thing that do look suspicious is that why are you sending Box<dyn FnOnce() -> Box<dyn Updater>>
instead of Box<dyn Updater>
itself? If OmsHelper::create_prepare_order
is very cheap then you can just call that in the main thread.