Lifetime of structure in lazy_static


#1

Hi ! Sorry for my terrible english.
I have no problem with this code before I add trait Performable to BotJob, after this i need to define lifetime for BotStatus and then to Tracker, after this lazy_static definition of Tracker do not compile, but I don’t known other way how to get access to Tracker in callback function loaded_finished.

Any advise ?

#[macro_use] extern crate cpython;
#[macro_use] extern crate lazy_static;

extern crate uuid;

use std::sync::{Arc, Mutex};
use std::collections::{VecDeque, HashMap};
use std::{thread, time};

use uuid::Uuid;
use cpython::{ GILGuard,  PyResult, ObjectProtocol,  PyObject, Python, NoArgs };


// ??? HOW TO ADD VALID LIFETIME FOR  TRACKER?
// lazy_static! {
 // pub static ref TRACKER : Arc<Mutex<Tracker>> = Arc::new(Mutex::new(Tracker::new())); 
// }



struct BotJob {
  url: String,
}


trait Performable {
  fn perform(&mut self, page: &mut Page);
}


impl BotJob {
  fn new() -> BotJob {
BotJob { 
  url: "https://www.google.com/".to_string(), 
}
  }
}

impl Performable for BotJob {
  fn perform(&mut self, page: &mut Page) {
page.load(&self.url)
  }
}

struct BotStatus<'x> {
  id: String,
  pub job: Option<Box<Performable + 'x>>,
}

impl<'x> BotStatus<'x> {
  fn new(id: String) -> BotStatus<'x> {
BotStatus { id: id, job: None}
  }

  fn add_job<T: Performable + 'x>(&mut self, job: T ) {
self.job = Some(Box::new(job));  
  }
}


struct Tracker<'x> {
  pub bots: HashMap<String, BotStatus<'x>>, 
}

impl<'x> Tracker<'x> {

  fn new() -> Tracker<'x> {
  Tracker { bots: HashMap::new() }
  }

  fn add_bot(&mut self) {
let id = Uuid::new_v4().to_string();
let mut status = BotStatus::new(id.clone());

let job = BotJob::new();
status.add_job(job);

self.bots.insert(id.clone(), status);

thread::spawn(move || {

  Bot::new(id);

});
  }
}


pub struct Page<'a> {
  py: Python<'a>,
  view: PyObject,
  page: PyObject,
  frame: PyObject,
  pub url: String,
  pub uuid: String,
}

impl<'a> Page<'a> {
  
  pub fn new(py: Python<'a>) -> Page<'a> {

let main = py.import("__main__").unwrap();
let view = main.get(py, "view").unwrap();
let uuid = main.get(py, "uuid").unwrap().to_string();

let page : PyObject = view.call_method(py, "page", NoArgs, None).unwrap().extract(py).unwrap();
let frame : PyObject = page.call_method(py, "mainFrame", NoArgs, None).unwrap().extract(py).unwrap();
let qurl : PyObject = view.call_method(py, "url", NoArgs, None).unwrap().extract(py).unwrap();
let url : String = qurl.call_method(py, "toString", NoArgs, None).unwrap().extract(py).unwrap();

Page {py: py, view: view, page: page, frame: frame, url: url, uuid: uuid }
  }

  pub fn load(&mut self, url: &str) { 
  let qtcore = self.py.import("PyQt5.QtCore").unwrap();
  let qurl = qtcore.get(self.py, "QUrl").unwrap();
  let _url : PyObject  = qurl.call(self.py, (url, ), None).unwrap().extract(self.py).unwrap();
  let _ : PyObject = self.view.call_method(self.py, "load", (_url, ), None).unwrap().extract(self.py).unwrap();
  }

  pub fn to_html(&self) -> String {
let data : PyObject = self.frame.call_method(self.py, "toHtml", NoArgs, None).unwrap().extract(self.py).unwrap();
data.to_string()
  }

}

pub struct Bot {
app: PyObject,  
window: PyObject,
gil: GILGuard,
}

impl Bot {

pub fn new(uuid: String) -> Bot {

  let argv: Vec<String> = vec![];
  let gil = Python::acquire_gil();
  let (app, w) = {

    let py : Python = gil.python();

    let main = py.import("__main__").unwrap();

    let qtcore = py.import("PyQt5.QtCore").unwrap();
    let qtwidgets = py.import("PyQt5.QtWidgets").unwrap();
    let qtwebkit = py.import("PyQt5.QtWebKitWidgets").unwrap();

    let qtapp = qtwidgets.get(py, "QApplication").unwrap();
    let qurl = qtcore.get(py, "QUrl").unwrap();
    let qmainwindow = qtwidgets.get(py, "QMainWindow").unwrap();
    let qwebview = qtwebkit.get(py,"QWebView").unwrap();

    let app: PyObject = qtapp.call(py, (argv, ), None).unwrap().extract(py).unwrap();

    let w : PyObject = qmainwindow.call(py, NoArgs, None).unwrap().extract(py).unwrap();
    let _ : PyObject = w.call_method(py, "setWindowTitle", ("WebkitPyQt from Rust", ), None).unwrap().extract(py).unwrap();

      let url : PyObject  = qurl.call(py, ("blank", ), None).unwrap().extract(py).unwrap();
      let view : PyObject = qwebview.call(py, (&w, ), None).unwrap().extract(py).unwrap(); 
      let _ : PyObject = view.getattr(py, "loadFinished").unwrap().call_method(py, "connect", (py_fn!(py, loaded_finished()), ), None).unwrap().extract(py).unwrap();
      let _ : PyObject = view.call_method(py, "load", (&url, ), None).unwrap().extract(py).unwrap();
      let _ : PyObject = view.call_method(py, "show", NoArgs, None).unwrap().extract(py).unwrap();

    let _ : PyObject = w.call_method(py, "setCentralWidget", (&view, ), None).unwrap().extract(py).unwrap();
    let _ : PyObject = w.call_method(py, "show", NoArgs, None).unwrap().extract(py).unwrap();

    let _ = main.add(py, "view", view); 
    let _ = main.add(py, "uuid",  uuid);

    let _ : PyObject = app.call_method(py, "exec", NoArgs, None).unwrap().extract(py).unwrap();

    (app, w)
  };

  Bot { app: app, gil: gil, window: w  }
}
}

fn loaded_finished(py: Python) -> PyResult<PyObject> {

let page = Page::new(py);

println!("HOW GET HERE TRACKER INSTANCE WITHOUT lazy_static OR HOW TO ADD VALID LIFETIME THERE?");

// let tracker = TRACKER.clone()
// tracker.bots.get_mut(&page.uuid).job.perform(&page);

Ok(py.None())
}


fn main() {
  let tracker = Arc::new(Mutex::new(Tracker::new())); 

  tracker.lock().unwrap().add_bot();

   thread::spawn(move || {
loop {
   thread::sleep(time::Duration::from_secs(1));
}
  }).join();
}

#2

The only valid lifetime you can use there is 'static. Narrower lifetimes only really work with stack bindings, and your Tracker isn’t tied to any stack frame once it’s in static storage.

I don’t understand the control flow here well enough to comment on a solution without lazy_static.